Skip to content

Commit 2478f20

Browse files
authored
jqwik property-based testing for JTD validation JtdPropertyTest (#100)
1 parent 5e03175 commit 2478f20

File tree

17 files changed

+808
-818
lines changed

17 files changed

+808
-818
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
for k in totals: totals[k]+=int(r.get(k,'0'))
4040
except Exception:
4141
pass
42-
exp_tests=466
42+
exp_tests=475
4343
exp_skipped=0
4444
if totals['tests']!=exp_tests or totals['skipped']!=exp_skipped:
4545
print(f"Unexpected test totals: {totals} != expected tests={exp_tests}, skipped={exp_skipped}")

AGENTS.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,26 @@
1414
- Never commit unverified mass changes—compile or test first.
1515
- Do not use Perl or sed for multi-line structural edits; rely on Python 3.2-friendly heredocs.
1616

17+
## Markdown-Driven-Development (MDD)
18+
We practice **Markdown-Driven-Development** where documentation precedes implementation:
19+
20+
1. **Create GitHub issue** with clear problem statement and goals
21+
2. **Update user documentation** (README.md) with new behavior/semantics
22+
3. **Update agentic documentation** (AGENTS.md) with implementation guidance
23+
4. **Update specialist documentation** (**/*.md, e.g., ARCHITECTURE.md) as needed
24+
5. **Create implementation plan** (PLAN_${issue_id}.md) documenting exact changes
25+
6. **Implement code changes** to match documented behavior
26+
7. **Update tests** to validate the documented behavior
27+
8. **Verify all documentation** remains accurate after implementation
28+
29+
This ensures:
30+
- Users understand behavior changes before code is written
31+
- Developers have clear implementation guidance
32+
- Documentation stays synchronized with code
33+
- Breaking changes are clearly communicated
34+
35+
When making changes, always update documentation files before modifying code.
36+
1737

1838
## Testing & Logging Discipline
1939

@@ -222,11 +242,21 @@ The property test logs at FINEST level:
222242
### Issue Management
223243
- Use the native tooling for the remote (for example `gh` for GitHub).
224244
- Create issues in the repository tied to the `origin` remote unless instructed otherwise; if another remote is required, ask for its name.
225-
- Tickets and issues must state only what and why, leaving how for later discussion.
245+
- Tickets and issues must state only "what" and "why," leaving "how" for later discussion.
226246
- Comments may discuss implementation details.
227247
- Label tickets as `Ready` once actionable; if a ticket lacks that label, request confirmation before proceeding.
228248
- Limit tidy-up issues to an absolute minimum (no more than two per PR).
229249

250+
### Creating GitHub Issues
251+
- **Title requirements**: No issue numbers, no special characters, no quotes, no shell metacharacters
252+
- **Body requirements**: Write issue body to a file first, then use --body-file flag
253+
- **Example workflow**:
254+
```bash
255+
echo "Issue description here" > /tmp/issue_body.md
256+
gh issue create --title "Brief description of bug" --body-file /tmp/issue_body.md
257+
```
258+
- **Never use --body flag** with complex content - always use --body-file to avoid shell escaping issues
259+
230260
### Commit Requirements
231261
- Commit messages start with `Issue #<issue number> <short description>`.
232262
- Include a link to the referenced issue when possible.
@@ -488,6 +518,16 @@ IMPORTANT: Never disable tests written for logic that we are yet to write we do
488518
* Virtual threads for concurrent processing
489519
* **Use try-with-resources for all AutoCloseable resources** (HttpClient, streams, etc.)
490520

521+
## RFC 8927 Compliance Guidelines
522+
523+
* **{} must compile to the Empty form and accept any JSON value** (RFC 8927 §2.2)
524+
* **Do not introduce compatibility modes that reinterpret {} with object semantics**
525+
* **Specs from json-typedef-spec are authoritative for behavior and tests**
526+
* **If a test, doc, or code disagrees with RFC 8927 about {}, the test/doc/code is wrong**
527+
* **We log at INFO when {} is compiled to help users who come from non-JTD validators**
528+
529+
Per RFC 8927 §3.3.1: "If a schema is of the 'empty' form, then it accepts all instances. A schema of the 'empty' form will never produce any error indicators."
530+
491531
## Package Structure
492532

493533
* Use default (package-private) access as the standard. Do not use 'private' or 'public' by default.

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,19 @@ This repo contains an incubating JTD validator that has the core JSON API as its
295295

296296
A complete JSON Type Definition validator is included (module: json-java21-jtd).
297297

298+
### Empty Schema `{}` Semantics (RFC 8927)
299+
300+
Per **RFC 8927 (JSON Typedef)**, the empty schema `{}` is the **empty form** and
301+
**accepts all JSON instances** (null, boolean, numbers, strings, arrays, objects).
302+
303+
> RFC 8927 §2.2 "Forms":
304+
> `schema = empty / ref / type / enum / elements / properties / values / discriminator / definitions`
305+
> `empty = {}`
306+
> **Empty form:** A schema in the empty form accepts all JSON values and produces no errors.
307+
308+
⚠️ Note: Some tools or in-house validators mistakenly interpret `{}` as "object with no
309+
properties allowed." **That is not JTD.** This implementation follows RFC 8927 strictly.
310+
298311
```java
299312
import json.java21.jtd.Jtd;
300313
import jdk.sandbox.java.util.json.*;

json-java21-jtd/ARCHITECTURE.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,14 @@ $(command -v mvnd || command -v mvn || command -v ./mvnw) test -pl json-java21-j
288288
- **Definitions**: Validate all definitions exist at compile time
289289
- **Type Checking**: Strict RFC 8927 compliance for all primitive types
290290

291+
## Empty Schema `{}`
292+
293+
- **Form**: `empty = {}`
294+
- **Behavior**: **accepts all instances**; produces no validation errors.
295+
- **RFC 8927 §3.3.1**: "If a schema is of the 'empty' form, then it accepts all instances. A schema of the 'empty' form will never produce any error indicators."
296+
- **Common pitfall**: confusing JTD with non-JTD validators that treat `{}` as an empty-object schema.
297+
- **Implementation**: compile `{}` to `EmptySchema` and validate everything as OK.
298+
291299
## RFC 8927 Compliance
292300

293301
This implementation strictly follows RFC 8927:
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package json.java21.jtd;
2+
3+
/// Lightweight breadcrumb trail for human-readable error paths
4+
record Crumbs(String value) {
5+
static Crumbs root() {
6+
return new Crumbs("#");
7+
}
8+
9+
Crumbs withObjectField(String name) {
10+
return new Crumbs(value + "→field:" + name);
11+
}
12+
13+
Crumbs withArrayIndex(int idx) {
14+
return new Crumbs(value + "→item:" + idx);
15+
}
16+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package json.java21.jtd;
2+
3+
import jdk.sandbox.java.util.json.JsonValue;
4+
5+
/// Stack frame for iterative validation with path and offset tracking
6+
record Frame(JtdSchema schema, JsonValue instance, String ptr, Crumbs crumbs, String discriminatorKey) {
7+
/// Constructor for normal validation without discriminator context
8+
Frame(JtdSchema schema, JsonValue instance, String ptr, Crumbs crumbs) {
9+
this(schema, instance, ptr, crumbs, null);
10+
}
11+
12+
@Override
13+
public String toString() {
14+
final var kind = schema.getClass().getSimpleName();
15+
final var tag = (schema instanceof JtdSchema.RefSchema r) ? "(ref=" + r.ref() + ")" : "";
16+
return "Frame[schema=" + kind + tag + ", instance=" + instance + ", ptr=" + ptr +
17+
", crumbs=" + crumbs + ", discriminatorKey=" + discriminatorKey + "]";
18+
}
19+
}

0 commit comments

Comments
 (0)