Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,26 @@ jobs:
- name: Build and verify
run: mvn -B -DskipITs=false -DskipTests=false verify

- name: Assert test count (no tests silently skipped)
run: |
python3 - <<'PY'
import os, xml.etree.ElementTree as ET, sys
totals={'tests':0,'failures':0,'errors':0,'skipped':0}
for dirpath,_,files in os.walk('.'):
if 'target' not in dirpath: continue
if 'surefire-reports' not in dirpath and 'failsafe-reports' not in dirpath: continue
for fn in files:
if not fn.endswith('.xml'): continue
p=os.path.join(dirpath,fn)
try:
r=ET.parse(p).getroot()
for k in totals: totals[k]+=int(r.get(k,'0'))
except Exception:
pass
exp_tests=1807
exp_skipped=577
if totals['tests']!=exp_tests or totals['skipped']!=exp_skipped:
print(f"Unexpected test totals: {totals} != expected tests={exp_tests}, skipped={exp_skipped}")
sys.exit(1)
print(f"OK totals: {totals}")
PY
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ PY
### json-java21-schema
- **Validator** for JSON Schema 2020-12 features
- **Tests** include unit, integration, and annotation-based checks (see module guide)
- **OpenRPC IT**: See `json-java21-schema/src/test/java/io/github/simbo1905/json/schema/OpenRPCSchemaValidationIT.java` and resources under `json-java21-schema/src/test/resources/openrpc/` (thanks to OpenRPC meta-schema and examples, Apache-2.0).

## Security Notes
- **Stack exhaustion attacks**: Deep nesting can cause StackOverflowError
Expand Down
5 changes: 5 additions & 0 deletions json-java21-schema/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ The project uses `java.util.logging` with levels:
- **Real-world schemas**: Complex nested validation scenarios
- **Performance tests**: Large schema compilation

#### OpenRPC Validation (`OpenRPCSchemaValidationIT.java`)
- **Location**: `json-java21-schema/src/test/java/io/github/simbo1905/json/schema/OpenRPCSchemaValidationIT.java`
- **Resources**: `src/test/resources/openrpc/schema.json` and `openrpc/examples/*.json`
- **Thanks**: OpenRPC meta-schema and examples (Apache-2.0). Sources: https://github.com/open-rpc/meta-schema and https://github.com/open-rpc/examples

#### Annotation Tests (`JsonSchemaAnnotationsTest.java`)
- **Annotation processing**: Compile-time schema generation
- **Custom constraints**: Business rule validation
Expand Down
7 changes: 7 additions & 0 deletions json-java21-schema/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ mvn -pl json-java21-schema -am verify
mvn -Djson.schema.strict=true -pl json-java21-schema -am verify
```

OpenRPC validation

- Additional integration test validates OpenRPC documents using a minimal, self‑contained schema:
- Test: `src/test/java/io/github/simbo1905/json/schema/OpenRPCSchemaValidationIT.java`
- Resources: `src/test/resources/openrpc/` (schema and examples)
- Thanks to OpenRPC meta-schema and examples (Apache-2.0): https://github.com/open-rpc/meta-schema and https://github.com/open-rpc/examples

## API Design

Single public interface with all schema types as inner records:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.github.simbo1905.json.schema;

import jdk.sandbox.java.util.json.Json;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Integration tests: validate OpenRPC documents using a minimal embedded meta-schema.
*
* Resources:
* - Schema: src/test/resources/openrpc/schema.json
* - Examples: src/test/resources/openrpc/examples/*.json
* Files containing "-bad-" are intentionally invalid and must fail validation.
*/
public class OpenRPCSchemaValidationIT {

private static String readResource(String name) throws IOException {
try {
URL url = Objects.requireNonNull(OpenRPCSchemaValidationIT.class.getClassLoader().getResource(name), name);
return Files.readString(Path.of(url.toURI()), StandardCharsets.UTF_8);
} catch (URISyntaxException e) {
throw new IOException(e);
}
}

@TestFactory
Stream<DynamicTest> validateOpenRPCExamples() throws Exception {
// Compile the minimal OpenRPC schema (self-contained, no remote $ref)
String schemaJson = readResource("openrpc/schema.json");
JsonSchema schema = JsonSchema.compile(Json.parse(schemaJson));

// Discover example files
URL dirUrl = Objects.requireNonNull(getClass().getClassLoader().getResource("openrpc/examples"),
"missing openrpc examples directory");
Path dir = Path.of(dirUrl.toURI());

try (Stream<Path> files = Files.list(dir)) {
List<Path> jsons = files
.filter(p -> p.getFileName().toString().endsWith(".json"))
.sorted()
.toList();

assertThat(jsons).isNotEmpty();

return jsons.stream().map(path -> DynamicTest.dynamicTest(path.getFileName().toString(), () -> {
String doc = Files.readString(path, StandardCharsets.UTF_8);
boolean expectedValid = !path.getFileName().toString().contains("-bad-");
boolean actualValid = schema.validate(Json.parse(doc)).valid();
Assertions.assertThat(actualValid)
.as("validation of %s", path.getFileName())
.isEqualTo(expectedValid);
}));
}
}
}

12 changes: 12 additions & 0 deletions json-java21-schema/src/test/resources/openrpc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
OpenRPC test resources

Provenance and license
- Source (meta‑schema): https://github.com/open-rpc/meta-schema (Apache-2.0)
- Source (examples): https://github.com/open-rpc/examples (Apache-2.0)

These files are copied verbatim or lightly adapted for fair use in research and education to test the JSON Schema validator in this repository. See the original repositories for authoritative copies and full license terms.

Notes
- The `schema.json` here is a minimal, self‑contained subset of the OpenRPC meta‑schema focused on validating overall document shape used by the included examples. It intentionally avoids external `$ref` to remain compatible with the current validator (which supports local `$ref`).
- Example documents live under `examples/`. Files containing `-bad-` are intentionally invalid variants used for negative tests.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"openrpc": "1.2.4",
"methods": []
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"openrpc": 1.2,
"info": {
"title": "",
"version": "1.0.0"
},
"methods": []
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"openrpc": "1.2.4",
"info": {
"title": ""
},
"methods": []
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"openrpc": "1.2.4",
"info": {
"title": "",
"version": "1.0.0"
},
"methods": {}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"openrpc": "1.2.4",
"info": {
"title": "",
"version": "1.0.0"
},
"methods": []
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"openrpc": "1.3.0",
"info": {
"title": "Metrics",
"description": "An example of a metrics service that uses notification-only methods",
"version": "1.0.0"
},
"servers": [],
"methods": [
{
"name": "link_clicked",
"params": [
{
"name": "link href",
"schema": {
"title": "href",
"type": "string",
"format": "uri"
}
},
{
"name": "link label",
"schema": {
"title": "label",
"type": "string"
}
}
],
"examples": [
{
"name": "login link clicked",
"params": [
{ "name": "link href", "value": "https://open-rpc.org" },
{ "name": "link label", "value": "Visit the OpenRPC Homepage" }
]
}
]
}
]
}

56 changes: 56 additions & 0 deletions json-java21-schema/src/test/resources/openrpc/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.test/openrpc-minimal",
"title": "OpenRPC (minimal subset for tests)",
"type": "object",
"additionalProperties": true,
"required": ["openrpc", "info", "methods"],
"properties": {
"openrpc": { "type": "string", "minLength": 1 },
"info": {
"type": "object",
"additionalProperties": true,
"required": ["title", "version"],
"properties": {
"title": { "type": "string" },
"version": { "type": "string" },
"description": { "type": "string" },
"termsOfService": { "type": "string" }
}
},
"methods": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"required": ["name", "params"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"summary": { "type": "string" },
"description": { "type": "string" },
"params": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": true,
"required": ["name"],
"properties": {
"name": { "type": "string", "minLength": 1 },
"schema": { "type": "object" }
}
}
},
"examples": { "type": "array" },
"errors": { "type": "array" },
"links": { "type": "array" },
"tags": { "type": "array" }
}
}
},
"servers": { "type": "array" },
"components": { "type": "object" },
"externalDocs": { "type": "object" },
"$schema": { "type": "string" }
}
}

Loading