Compare commits
5 Commits
8e50d4852d
...
1.0.49
| Author | SHA1 | Date | |
|---|---|---|---|
| 612188a54b | |||
| 29c5160b49 | |||
| 944675d669 | |||
| 53a40d1099 | |||
| e55977c11b |
36
GEMINI.md
36
GEMINI.md
@ -18,7 +18,7 @@ The extension exposes the following functions to PostgreSQL:
|
|||||||
|
|
||||||
### `cache_json_schemas(enums jsonb, types jsonb, puncs jsonb) -> jsonb`
|
### `cache_json_schemas(enums jsonb, types jsonb, puncs jsonb) -> jsonb`
|
||||||
|
|
||||||
Loads and compiles the entire schema registry into the session's memory.
|
Loads and compiles the entire schema registry into the session's memory, atomically replacing the previous validator.
|
||||||
|
|
||||||
* **Inputs**:
|
* **Inputs**:
|
||||||
* `enums`: Array of enum definitions.
|
* `enums`: Array of enum definitions.
|
||||||
@ -31,6 +31,17 @@ Loads and compiles the entire schema registry into the session's memory.
|
|||||||
* Compiles schemas into validators.
|
* Compiles schemas into validators.
|
||||||
* **Returns**: `{"response": "success"}` or an error object.
|
* **Returns**: `{"response": "success"}` or an error object.
|
||||||
|
|
||||||
|
### `mask_json_schema(schema_id text, instance jsonb) -> jsonb`
|
||||||
|
|
||||||
|
Validates a JSON instance and returns a new JSON object with unknown properties removed (pruned) based on the schema.
|
||||||
|
|
||||||
|
* **Inputs**:
|
||||||
|
* `schema_id`: The `$id` of the schema to mask against.
|
||||||
|
* `instance`: The JSON data to mask.
|
||||||
|
* **Returns**:
|
||||||
|
* On success: A `Drop` containing the **masked data**.
|
||||||
|
* On failure: A `Drop` containing validation errors.
|
||||||
|
|
||||||
### `validate_json_schema(schema_id text, instance jsonb) -> jsonb`
|
### `validate_json_schema(schema_id text, instance jsonb) -> jsonb`
|
||||||
|
|
||||||
Validates a JSON instance against a pre-compiled schema.
|
Validates a JSON instance against a pre-compiled schema.
|
||||||
@ -80,10 +91,19 @@ JSPG enforces a "Secure by Default" philosophy. All schemas are treated as if `u
|
|||||||
* **Ref Boundaries**: Strictness is reset when crossing `$ref` boundaries. The referenced schema's strictness is determined by its own definition (strict by default unless `extensible: true`), ignoring the caller's state.
|
* **Ref Boundaries**: Strictness is reset when crossing `$ref` boundaries. The referenced schema's strictness is determined by its own definition (strict by default unless `extensible: true`), ignoring the caller's state.
|
||||||
* **Inheritance**: Strictness is inherited. A schema extending a strict parent will also be strict unless it declares itself `extensible: true`. Conversely, a schema extending a loose parent will also be loose unless it declares itself `extensible: false`.
|
* **Inheritance**: Strictness is inherited. A schema extending a strict parent will also be strict unless it declares itself `extensible: true`. Conversely, a schema extending a loose parent will also be loose unless it declares itself `extensible: false`.
|
||||||
|
|
||||||
|
|
||||||
### 4. Format Leniency for Empty Strings
|
### 4. Format Leniency for Empty Strings
|
||||||
To simplify frontend form logic, the format validators for `uuid`, `date-time`, and `email` explicitly allow empty strings (`""`). This treats an empty string as "present but unset" rather than "invalid format".
|
To simplify frontend form logic, the format validators for `uuid`, `date-time`, and `email` explicitly allow empty strings (`""`). This treats an empty string as "present but unset" rather than "invalid format".
|
||||||
|
|
||||||
|
### 5. Masking (Constructive Validation)
|
||||||
|
JSPG supports a "Constructive Validation" mode via `mask_json_schema`. This is designed for high-performance API responses where the schema dictates the exact shape of the returned data.
|
||||||
|
|
||||||
|
* **Mechanism**: The validator traverses the instance against the schema.
|
||||||
|
* **Valid Fields**: Kept in the output.
|
||||||
|
* **Unknown/Extra Fields**: Silently removed (pruned) if `extensible: false` (default).
|
||||||
|
* **Invalid Fields**: Still trigger standard validation errors.
|
||||||
|
|
||||||
|
This allows the database to return "raw" joined rows (e.g. `SELECT * FROM person JOIN organization ...`) and have JSPG automatically shape the result into the expected API response, removing any internal or unrelated columns not defined in the schema.
|
||||||
|
|
||||||
## 🏗️ Architecture
|
## 🏗️ Architecture
|
||||||
|
|
||||||
The extension is written in Rust using `pgrx` and structures its schema parser to mirror the Punc Generator's design:
|
The extension is written in Rust using `pgrx` and structures its schema parser to mirror the Punc Generator's design:
|
||||||
@ -92,8 +112,18 @@ The extension is written in Rust using `pgrx` and structures its schema parser t
|
|||||||
* **Compiler Phase**: schema JSONs are parsed into this struct, linked (references resolved), and then compiled into an efficient validation tree.
|
* **Compiler Phase**: schema JSONs are parsed into this struct, linked (references resolved), and then compiled into an efficient validation tree.
|
||||||
* **Validation Phase**: The compiled validators traverse the JSON instance using `serde_json::Value`.
|
* **Validation Phase**: The compiled validators traverse the JSON instance using `serde_json::Value`.
|
||||||
|
|
||||||
|
### Concurrency & Threading ("Atomic Swap")
|
||||||
|
|
||||||
|
To support high-throughput validation while allowing for runtime schema updates (e.g., during development or hot-reloading), JSPG uses an **Atomic Swap** pattern.
|
||||||
|
|
||||||
|
1. **Immutable Validator**: The `Validator` struct immutably owns the `Registry`. Once created, a validator instance (and its registry) never changes.
|
||||||
|
2. **Global Pointer**: A global `RwLock<Option<Arc<Validator>>>` holds the current active validator.
|
||||||
|
3. **Lock-Free Reads**: Validation requests acquire a read lock just long enough to clone the `Arc` (incrementing a reference count), then release the lock immediately. Validation proceeds on the snapshot, ensuring no blocking during schema updates.
|
||||||
|
4. **Atomic Updates**: When schemas are reloaded (`cache_json_schemas`), a new `Registry` and `Validator` are built entirely on the stack. The global pointer is then atomically swapped to the new instance under a write lock.
|
||||||
|
|
||||||
## 🧪 Testing
|
## 🧪 Testing
|
||||||
|
|
||||||
Testing is driven by standard Rust unit tests that load JSON fixtures.
|
Testing is driven by standard Rust unit tests that load JSON fixtures.
|
||||||
|
|
||||||
The tests are located in `tests/fixtures/*.json` and are executed via `cargo test`.
|
* **Isolation**: Each test file runs with its own isolated `Registry` and `Validator` instance, created on the stack. This eliminates global state interference and allows tests to run in parallel.
|
||||||
|
* **Fixtures**: The tests are located in `tests/fixtures/*.json` and are executed via `cargo test`.
|
||||||
813
debug.log
813
debug.log
@ -1,813 +0,0 @@
|
|||||||
warning: function `test_uniqueItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:52:4
|
|
||||||
|
|
|
||||||
52 | fn test_uniqueItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_0`
|
|
||||||
|
|
|
||||||
= note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:58:4
|
|
||||||
|
|
|
||||||
58 | fn test_uniqueItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_1`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:64:4
|
|
||||||
|
|
|
||||||
64 | fn test_uniqueItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_2`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:70:4
|
|
||||||
|
|
|
||||||
70 | fn test_uniqueItems_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_3`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:76:4
|
|
||||||
|
|
|
||||||
76 | fn test_uniqueItems_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_4`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:82:4
|
|
||||||
|
|
|
||||||
82 | fn test_uniqueItems_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_5`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:88:4
|
|
||||||
|
|
|
||||||
88 | fn test_uniqueItems_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_6`
|
|
||||||
|
|
||||||
warning: function `test_minItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:94:4
|
|
||||||
|
|
|
||||||
94 | fn test_minItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_0`
|
|
||||||
|
|
||||||
warning: function `test_minItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:100:4
|
|
||||||
|
|
|
||||||
100 | fn test_minItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_1`
|
|
||||||
|
|
||||||
warning: function `test_minItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:106:4
|
|
||||||
|
|
|
||||||
106 | fn test_minItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_2`
|
|
||||||
|
|
||||||
warning: function `test_exclusiveMinimum_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:160:4
|
|
||||||
|
|
|
||||||
160 | fn test_exclusiveMinimum_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_exclusive_minimum_0`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:274:4
|
|
||||||
|
|
|
||||||
274 | fn test_anyOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_0`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:280:4
|
|
||||||
|
|
|
||||||
280 | fn test_anyOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_1`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:286:4
|
|
||||||
|
|
|
||||||
286 | fn test_anyOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_2`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:292:4
|
|
||||||
|
|
|
||||||
292 | fn test_anyOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_3`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:298:4
|
|
||||||
|
|
|
||||||
298 | fn test_anyOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_4`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:304:4
|
|
||||||
|
|
|
||||||
304 | fn test_anyOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_5`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:310:4
|
|
||||||
|
|
|
||||||
310 | fn test_anyOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_6`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:316:4
|
|
||||||
|
|
|
||||||
316 | fn test_anyOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_7`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:322:4
|
|
||||||
|
|
|
||||||
322 | fn test_anyOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_8`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:328:4
|
|
||||||
|
|
|
||||||
328 | fn test_anyOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_9`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:334:4
|
|
||||||
|
|
|
||||||
334 | fn test_propertyNames_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_0`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:340:4
|
|
||||||
|
|
|
||||||
340 | fn test_propertyNames_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_1`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:346:4
|
|
||||||
|
|
|
||||||
346 | fn test_propertyNames_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_2`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:352:4
|
|
||||||
|
|
|
||||||
352 | fn test_propertyNames_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_3`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:358:4
|
|
||||||
|
|
|
||||||
358 | fn test_propertyNames_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_4`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:364:4
|
|
||||||
|
|
|
||||||
364 | fn test_propertyNames_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_5`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:370:4
|
|
||||||
|
|
|
||||||
370 | fn test_propertyNames_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_6`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:646:4
|
|
||||||
|
|
|
||||||
646 | fn test_minProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:652:4
|
|
||||||
|
|
|
||||||
652 | fn test_minProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:658:4
|
|
||||||
|
|
|
||||||
658 | fn test_minProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_minContains_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:664:4
|
|
||||||
|
|
|
||||||
664 | fn test_minContains_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_0`
|
|
||||||
|
|
||||||
warning: function `test_minContains_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:670:4
|
|
||||||
|
|
|
||||||
670 | fn test_minContains_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_1`
|
|
||||||
|
|
||||||
warning: function `test_minContains_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:676:4
|
|
||||||
|
|
|
||||||
676 | fn test_minContains_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_2`
|
|
||||||
|
|
||||||
warning: function `test_minContains_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:682:4
|
|
||||||
|
|
|
||||||
682 | fn test_minContains_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_3`
|
|
||||||
|
|
||||||
warning: function `test_minContains_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:688:4
|
|
||||||
|
|
|
||||||
688 | fn test_minContains_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_4`
|
|
||||||
|
|
||||||
warning: function `test_minContains_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:694:4
|
|
||||||
|
|
|
||||||
694 | fn test_minContains_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_5`
|
|
||||||
|
|
||||||
warning: function `test_minContains_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:700:4
|
|
||||||
|
|
|
||||||
700 | fn test_minContains_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_6`
|
|
||||||
|
|
||||||
warning: function `test_minContains_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:706:4
|
|
||||||
|
|
|
||||||
706 | fn test_minContains_7() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_7`
|
|
||||||
|
|
||||||
warning: function `test_minContains_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:712:4
|
|
||||||
|
|
|
||||||
712 | fn test_minContains_8() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_8`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:796:4
|
|
||||||
|
|
|
||||||
796 | fn test_maxContains_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_0`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:802:4
|
|
||||||
|
|
|
||||||
802 | fn test_maxContains_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_1`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:808:4
|
|
||||||
|
|
|
||||||
808 | fn test_maxContains_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_2`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:814:4
|
|
||||||
|
|
|
||||||
814 | fn test_maxContains_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_3`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:820:4
|
|
||||||
|
|
|
||||||
820 | fn test_maxContains_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_4`
|
|
||||||
|
|
||||||
warning: function `test_maxLength_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:826:4
|
|
||||||
|
|
|
||||||
826 | fn test_maxLength_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_length_0`
|
|
||||||
|
|
||||||
warning: function `test_maxLength_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:832:4
|
|
||||||
|
|
|
||||||
832 | fn test_maxLength_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_length_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:838:4
|
|
||||||
|
|
|
||||||
838 | fn test_dependentSchemas_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_0`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:844:4
|
|
||||||
|
|
|
||||||
844 | fn test_dependentSchemas_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:850:4
|
|
||||||
|
|
|
||||||
850 | fn test_dependentSchemas_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_2`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:856:4
|
|
||||||
|
|
|
||||||
856 | fn test_dependentSchemas_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_3`
|
|
||||||
|
|
||||||
warning: function `test_exclusiveMaximum_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:862:4
|
|
||||||
|
|
|
||||||
862 | fn test_exclusiveMaximum_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_exclusive_maximum_0`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:868:4
|
|
||||||
|
|
|
||||||
868 | fn test_prefixItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_0`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:874:4
|
|
||||||
|
|
|
||||||
874 | fn test_prefixItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_1`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:880:4
|
|
||||||
|
|
|
||||||
880 | fn test_prefixItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_2`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:886:4
|
|
||||||
|
|
|
||||||
886 | fn test_prefixItems_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_3`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:892:4
|
|
||||||
|
|
|
||||||
892 | fn test_prefixItems_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_4`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:910:4
|
|
||||||
|
|
|
||||||
910 | fn test_oneOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_0`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:916:4
|
|
||||||
|
|
|
||||||
916 | fn test_oneOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_1`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:922:4
|
|
||||||
|
|
|
||||||
922 | fn test_oneOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_2`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:928:4
|
|
||||||
|
|
|
||||||
928 | fn test_oneOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_3`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:934:4
|
|
||||||
|
|
|
||||||
934 | fn test_oneOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_4`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:940:4
|
|
||||||
|
|
|
||||||
940 | fn test_oneOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_5`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:946:4
|
|
||||||
|
|
|
||||||
946 | fn test_oneOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_6`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:952:4
|
|
||||||
|
|
|
||||||
952 | fn test_oneOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_7`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:958:4
|
|
||||||
|
|
|
||||||
958 | fn test_oneOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_8`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:964:4
|
|
||||||
|
|
|
||||||
964 | fn test_oneOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_9`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:970:4
|
|
||||||
|
|
|
||||||
970 | fn test_oneOf_10() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_10`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:976:4
|
|
||||||
|
|
|
||||||
976 | fn test_oneOf_11() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_11`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:982:4
|
|
||||||
|
|
|
||||||
982 | fn test_oneOf_12() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_12`
|
|
||||||
|
|
||||||
warning: function `test_emptyString_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1072:4
|
|
||||||
|
|
|
||||||
1072 | fn test_emptyString_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_empty_string_0`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1090:4
|
|
||||||
|
|
|
||||||
1090 | fn test_maxProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1096:4
|
|
||||||
|
|
|
||||||
1096 | fn test_maxProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1102:4
|
|
||||||
|
|
|
||||||
1102 | fn test_maxProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1108:4
|
|
||||||
|
|
|
||||||
1108 | fn test_maxProperties_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_3`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1114:4
|
|
||||||
|
|
|
||||||
1114 | fn test_dependentRequired_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_0`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1120:4
|
|
||||||
|
|
|
||||||
1120 | fn test_dependentRequired_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1126:4
|
|
||||||
|
|
|
||||||
1126 | fn test_dependentRequired_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_2`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1132:4
|
|
||||||
|
|
|
||||||
1132 | fn test_dependentRequired_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_3`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1138:4
|
|
||||||
|
|
|
||||||
1138 | fn test_dependentRequired_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_4`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1252:4
|
|
||||||
|
|
|
||||||
1252 | fn test_multipleOf_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_0`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1258:4
|
|
||||||
|
|
|
||||||
1258 | fn test_multipleOf_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_1`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1264:4
|
|
||||||
|
|
|
||||||
1264 | fn test_multipleOf_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_2`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1270:4
|
|
||||||
|
|
|
||||||
1270 | fn test_multipleOf_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_3`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1276:4
|
|
||||||
|
|
|
||||||
1276 | fn test_patternProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1282:4
|
|
||||||
|
|
|
||||||
1282 | fn test_patternProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1288:4
|
|
||||||
|
|
|
||||||
1288 | fn test_patternProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1294:4
|
|
||||||
|
|
|
||||||
1294 | fn test_patternProperties_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_3`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1300:4
|
|
||||||
|
|
|
||||||
1300 | fn test_patternProperties_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_4`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1306:4
|
|
||||||
|
|
|
||||||
1306 | fn test_patternProperties_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_5`
|
|
||||||
|
|
||||||
warning: function `test_allOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1336:4
|
|
||||||
|
|
|
||||||
1336 | fn test_allOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_0`
|
|
||||||
|
|
||||||
warning: function `test_allOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1342:4
|
|
||||||
|
|
|
||||||
1342 | fn test_allOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_1`
|
|
||||||
|
|
||||||
warning: function `test_allOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1348:4
|
|
||||||
|
|
|
||||||
1348 | fn test_allOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_2`
|
|
||||||
|
|
||||||
warning: function `test_allOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1354:4
|
|
||||||
|
|
|
||||||
1354 | fn test_allOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_3`
|
|
||||||
|
|
||||||
warning: function `test_allOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1360:4
|
|
||||||
|
|
|
||||||
1360 | fn test_allOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_4`
|
|
||||||
|
|
||||||
warning: function `test_allOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1366:4
|
|
||||||
|
|
|
||||||
1366 | fn test_allOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_5`
|
|
||||||
|
|
||||||
warning: function `test_allOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:1372:4
|
|
||||||
|
|
|
||||||
1372 | fn test_allOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_6`
|
|
||||||
|
|
||||||
warning: function `test_allOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:1378:4
|
|
||||||
|
|
|
||||||
1378 | fn test_allOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_7`
|
|
||||||
|
|
||||||
warning: function `test_allOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:1384:4
|
|
||||||
|
|
|
||||||
1384 | fn test_allOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_8`
|
|
||||||
|
|
||||||
warning: function `test_allOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:1390:4
|
|
||||||
|
|
|
||||||
1390 | fn test_allOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_9`
|
|
||||||
|
|
||||||
warning: function `test_allOf_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:1396:4
|
|
||||||
|
|
|
||||||
1396 | fn test_allOf_10() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_10`
|
|
||||||
|
|
||||||
warning: function `test_allOf_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:1402:4
|
|
||||||
|
|
|
||||||
1402 | fn test_allOf_11() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_11`
|
|
||||||
|
|
||||||
warning: function `test_allOf_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:1408:4
|
|
||||||
|
|
|
||||||
1408 | fn test_allOf_12() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_12`
|
|
||||||
|
|
||||||
warning: function `test_allOf_13` should have a snake case name
|
|
||||||
--> tests/tests.rs:1414:4
|
|
||||||
|
|
|
||||||
1414 | fn test_allOf_13() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_13`
|
|
||||||
|
|
||||||
warning: function `test_allOf_14` should have a snake case name
|
|
||||||
--> tests/tests.rs:1420:4
|
|
||||||
|
|
|
||||||
1420 | fn test_allOf_14() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_14`
|
|
||||||
|
|
||||||
warning: function `test_allOf_15` should have a snake case name
|
|
||||||
--> tests/tests.rs:1426:4
|
|
||||||
|
|
|
||||||
1426 | fn test_allOf_15() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_15`
|
|
||||||
|
|
||||||
warning: function `test_minLength_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1828:4
|
|
||||||
|
|
|
||||||
1828 | fn test_minLength_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_length_0`
|
|
||||||
|
|
||||||
warning: function `test_minLength_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1834:4
|
|
||||||
|
|
|
||||||
1834 | fn test_minLength_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_length_1`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1840:4
|
|
||||||
|
|
|
||||||
1840 | fn test_maxItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_0`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1846:4
|
|
||||||
|
|
|
||||||
1846 | fn test_maxItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_1`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1852:4
|
|
||||||
|
|
|
||||||
1852 | fn test_maxItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_2`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1912:4
|
|
||||||
|
|
|
||||||
1912 | fn test_dynamicRef_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_0`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1918:4
|
|
||||||
|
|
|
||||||
1918 | fn test_dynamicRef_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_1`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1924:4
|
|
||||||
|
|
|
||||||
1924 | fn test_dynamicRef_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_2`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1930:4
|
|
||||||
|
|
|
||||||
1930 | fn test_dynamicRef_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_3`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1936:4
|
|
||||||
|
|
|
||||||
1936 | fn test_dynamicRef_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_4`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1942:4
|
|
||||||
|
|
|
||||||
1942 | fn test_dynamicRef_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_5`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:1948:4
|
|
||||||
|
|
|
||||||
1948 | fn test_dynamicRef_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_6`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:1954:4
|
|
||||||
|
|
|
||||||
1954 | fn test_dynamicRef_7() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_7`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:1960:4
|
|
||||||
|
|
|
||||||
1960 | fn test_dynamicRef_8() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_8`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:1966:4
|
|
||||||
|
|
|
||||||
1966 | fn test_dynamicRef_9() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_9`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:1972:4
|
|
||||||
|
|
|
||||||
1972 | fn test_dynamicRef_10() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_10`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:1978:4
|
|
||||||
|
|
|
||||||
1978 | fn test_dynamicRef_11() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_11`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:1984:4
|
|
||||||
|
|
|
||||||
1984 | fn test_dynamicRef_12() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_12`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_13` should have a snake case name
|
|
||||||
--> tests/tests.rs:1990:4
|
|
||||||
|
|
|
||||||
1990 | fn test_dynamicRef_13() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_13`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_14` should have a snake case name
|
|
||||||
--> tests/tests.rs:1996:4
|
|
||||||
|
|
|
||||||
1996 | fn test_dynamicRef_14() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_14`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_15` should have a snake case name
|
|
||||||
--> tests/tests.rs:2002:4
|
|
||||||
|
|
|
||||||
2002 | fn test_dynamicRef_15() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_15`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_16` should have a snake case name
|
|
||||||
--> tests/tests.rs:2008:4
|
|
||||||
|
|
|
||||||
2008 | fn test_dynamicRef_16() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_16`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_17` should have a snake case name
|
|
||||||
--> tests/tests.rs:2014:4
|
|
||||||
|
|
|
||||||
2014 | fn test_dynamicRef_17() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_17`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_18` should have a snake case name
|
|
||||||
--> tests/tests.rs:2020:4
|
|
||||||
|
|
|
||||||
2020 | fn test_dynamicRef_18() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_18`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_19` should have a snake case name
|
|
||||||
--> tests/tests.rs:2026:4
|
|
||||||
|
|
|
||||||
2026 | fn test_dynamicRef_19() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_19`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_20` should have a snake case name
|
|
||||||
--> tests/tests.rs:2032:4
|
|
||||||
|
|
|
||||||
2032 | fn test_dynamicRef_20() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_20`
|
|
||||||
|
|
||||||
warning: `jspg` (test "tests") generated 132 warnings
|
|
||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.42s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
|
|
||||||
thread 'test_ref_39' (14864151) panicked at tests/tests.rs:1812:45:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[implicit keyword shadowing] Test 'child type overrides parent type' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'age'\", details: ErrorDetails { path: \"/age\" } }]\n[implicit keyword shadowing] Test 'parent max age (20) is shadowed (replaced) by child definition' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'age'\", details: ErrorDetails { path: \"/age\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_ref_39 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_ref_39
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.01s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
44
debug_2.log
44
debug_2.log
@ -1,44 +0,0 @@
|
|||||||
Blocking waiting for file lock on artifact directory
|
|
||||||
Compiling jspg v0.1.0 (/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg)
|
|
||||||
error[E0424]: expected value, found module `self`
|
|
||||||
--> src/util.rs:162:33
|
|
||||||
|
|
|
||||||
40 | pub fn run_test_file_at_index(path: &str, index: usi...
|
|
||||||
| ---------------------- this function can't have a `self` parameter
|
|
||||||
...
|
|
||||||
162 | let mut new_overrides = self.overrides.clone();
|
|
||||||
| ^^^^ `self` value is a keyword only available in methods with a `self` parameter
|
|
||||||
|
|
||||||
error[E0424]: expected value, found module `self`
|
|
||||||
--> src/util.rs:164:31
|
|
||||||
|
|
|
||||||
40 | pub fn run_test_file_at_index(path: &str, index: usi...
|
|
||||||
| ---------------------- this function can't have a `self` parameter
|
|
||||||
...
|
|
||||||
164 | if let Some(props) = &self.schema.properties {
|
|
||||||
| ^^^^ `self` value is a keyword only available in methods with a `self` parameter
|
|
||||||
|
|
||||||
error[E0282]: type annotations needed
|
|
||||||
--> src/util.rs:166:32
|
|
||||||
|
|
|
||||||
166 | new_overrides.extend(props.keys().cloned());
|
|
||||||
| ^^^^^ cannot infer type
|
|
||||||
|
|
||||||
error[E0599]: no method named `is_valid` found for struct `drop::Drop` in the current scope
|
|
||||||
--> src/util.rs:204:18
|
|
||||||
|
|
|
||||||
204 | ...ult.is_valid(), // Use is_valid() for clear "Got"...
|
|
||||||
| ^^^^^^^^ method not found in `drop::Drop`
|
|
||||||
|
|
|
||||||
::: src/drop.rs:5:1
|
|
||||||
|
|
|
||||||
5 | pub struct Drop {
|
|
||||||
| --------------- method `is_valid` not found for this struct
|
|
||||||
|
|
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
|
||||||
= note: the following trait defines an item `is_valid`, perhaps you need to implement it:
|
|
||||||
candidate #1: `NullLayout`
|
|
||||||
|
|
||||||
Some errors have detailed explanations: E0282, E0424, E0599.
|
|
||||||
For more information about an error, try `rustc --explain E0282`.
|
|
||||||
error: could not compile `jspg` (lib) due to 4 previous errors
|
|
||||||
815
debug_3.log
815
debug_3.log
@ -1,815 +0,0 @@
|
|||||||
Blocking waiting for file lock on artifact directory
|
|
||||||
Compiling jspg v0.1.0 (/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg)
|
|
||||||
warning: function `test_uniqueItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:52:4
|
|
||||||
|
|
|
||||||
52 | fn test_uniqueItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_0`
|
|
||||||
|
|
|
||||||
= note: `#[warn(non_snake_case)]` (part of `#[warn(nonstandard_style)]`) on by default
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:58:4
|
|
||||||
|
|
|
||||||
58 | fn test_uniqueItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_1`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:64:4
|
|
||||||
|
|
|
||||||
64 | fn test_uniqueItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_2`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:70:4
|
|
||||||
|
|
|
||||||
70 | fn test_uniqueItems_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_3`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:76:4
|
|
||||||
|
|
|
||||||
76 | fn test_uniqueItems_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_4`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:82:4
|
|
||||||
|
|
|
||||||
82 | fn test_uniqueItems_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_5`
|
|
||||||
|
|
||||||
warning: function `test_uniqueItems_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:88:4
|
|
||||||
|
|
|
||||||
88 | fn test_uniqueItems_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_unique_items_6`
|
|
||||||
|
|
||||||
warning: function `test_minItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:94:4
|
|
||||||
|
|
|
||||||
94 | fn test_minItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_0`
|
|
||||||
|
|
||||||
warning: function `test_minItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:100:4
|
|
||||||
|
|
|
||||||
100 | fn test_minItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_1`
|
|
||||||
|
|
||||||
warning: function `test_minItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:106:4
|
|
||||||
|
|
|
||||||
106 | fn test_minItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_items_2`
|
|
||||||
|
|
||||||
warning: function `test_exclusiveMinimum_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:160:4
|
|
||||||
|
|
|
||||||
160 | fn test_exclusiveMinimum_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_exclusive_minimum_0`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:274:4
|
|
||||||
|
|
|
||||||
274 | fn test_anyOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_0`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:280:4
|
|
||||||
|
|
|
||||||
280 | fn test_anyOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_1`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:286:4
|
|
||||||
|
|
|
||||||
286 | fn test_anyOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_2`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:292:4
|
|
||||||
|
|
|
||||||
292 | fn test_anyOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_3`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:298:4
|
|
||||||
|
|
|
||||||
298 | fn test_anyOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_4`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:304:4
|
|
||||||
|
|
|
||||||
304 | fn test_anyOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_5`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:310:4
|
|
||||||
|
|
|
||||||
310 | fn test_anyOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_6`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:316:4
|
|
||||||
|
|
|
||||||
316 | fn test_anyOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_7`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:322:4
|
|
||||||
|
|
|
||||||
322 | fn test_anyOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_8`
|
|
||||||
|
|
||||||
warning: function `test_anyOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:328:4
|
|
||||||
|
|
|
||||||
328 | fn test_anyOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_any_of_9`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:334:4
|
|
||||||
|
|
|
||||||
334 | fn test_propertyNames_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_0`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:340:4
|
|
||||||
|
|
|
||||||
340 | fn test_propertyNames_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_1`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:346:4
|
|
||||||
|
|
|
||||||
346 | fn test_propertyNames_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_2`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:352:4
|
|
||||||
|
|
|
||||||
352 | fn test_propertyNames_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_3`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:358:4
|
|
||||||
|
|
|
||||||
358 | fn test_propertyNames_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_4`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:364:4
|
|
||||||
|
|
|
||||||
364 | fn test_propertyNames_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_5`
|
|
||||||
|
|
||||||
warning: function `test_propertyNames_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:370:4
|
|
||||||
|
|
|
||||||
370 | fn test_propertyNames_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_property_names_6`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:646:4
|
|
||||||
|
|
|
||||||
646 | fn test_minProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:652:4
|
|
||||||
|
|
|
||||||
652 | fn test_minProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_minProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:658:4
|
|
||||||
|
|
|
||||||
658 | fn test_minProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_minContains_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:664:4
|
|
||||||
|
|
|
||||||
664 | fn test_minContains_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_0`
|
|
||||||
|
|
||||||
warning: function `test_minContains_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:670:4
|
|
||||||
|
|
|
||||||
670 | fn test_minContains_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_1`
|
|
||||||
|
|
||||||
warning: function `test_minContains_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:676:4
|
|
||||||
|
|
|
||||||
676 | fn test_minContains_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_2`
|
|
||||||
|
|
||||||
warning: function `test_minContains_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:682:4
|
|
||||||
|
|
|
||||||
682 | fn test_minContains_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_3`
|
|
||||||
|
|
||||||
warning: function `test_minContains_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:688:4
|
|
||||||
|
|
|
||||||
688 | fn test_minContains_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_4`
|
|
||||||
|
|
||||||
warning: function `test_minContains_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:694:4
|
|
||||||
|
|
|
||||||
694 | fn test_minContains_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_5`
|
|
||||||
|
|
||||||
warning: function `test_minContains_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:700:4
|
|
||||||
|
|
|
||||||
700 | fn test_minContains_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_6`
|
|
||||||
|
|
||||||
warning: function `test_minContains_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:706:4
|
|
||||||
|
|
|
||||||
706 | fn test_minContains_7() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_7`
|
|
||||||
|
|
||||||
warning: function `test_minContains_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:712:4
|
|
||||||
|
|
|
||||||
712 | fn test_minContains_8() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_contains_8`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:796:4
|
|
||||||
|
|
|
||||||
796 | fn test_maxContains_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_0`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:802:4
|
|
||||||
|
|
|
||||||
802 | fn test_maxContains_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_1`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:808:4
|
|
||||||
|
|
|
||||||
808 | fn test_maxContains_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_2`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:814:4
|
|
||||||
|
|
|
||||||
814 | fn test_maxContains_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_3`
|
|
||||||
|
|
||||||
warning: function `test_maxContains_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:820:4
|
|
||||||
|
|
|
||||||
820 | fn test_maxContains_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_contains_4`
|
|
||||||
|
|
||||||
warning: function `test_maxLength_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:826:4
|
|
||||||
|
|
|
||||||
826 | fn test_maxLength_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_length_0`
|
|
||||||
|
|
||||||
warning: function `test_maxLength_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:832:4
|
|
||||||
|
|
|
||||||
832 | fn test_maxLength_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_length_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:838:4
|
|
||||||
|
|
|
||||||
838 | fn test_dependentSchemas_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_0`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:844:4
|
|
||||||
|
|
|
||||||
844 | fn test_dependentSchemas_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:850:4
|
|
||||||
|
|
|
||||||
850 | fn test_dependentSchemas_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_2`
|
|
||||||
|
|
||||||
warning: function `test_dependentSchemas_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:856:4
|
|
||||||
|
|
|
||||||
856 | fn test_dependentSchemas_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_schemas_3`
|
|
||||||
|
|
||||||
warning: function `test_exclusiveMaximum_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:862:4
|
|
||||||
|
|
|
||||||
862 | fn test_exclusiveMaximum_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_exclusive_maximum_0`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:868:4
|
|
||||||
|
|
|
||||||
868 | fn test_prefixItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_0`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:874:4
|
|
||||||
|
|
|
||||||
874 | fn test_prefixItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_1`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:880:4
|
|
||||||
|
|
|
||||||
880 | fn test_prefixItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_2`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:886:4
|
|
||||||
|
|
|
||||||
886 | fn test_prefixItems_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_3`
|
|
||||||
|
|
||||||
warning: function `test_prefixItems_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:892:4
|
|
||||||
|
|
|
||||||
892 | fn test_prefixItems_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_prefix_items_4`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:910:4
|
|
||||||
|
|
|
||||||
910 | fn test_oneOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_0`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:916:4
|
|
||||||
|
|
|
||||||
916 | fn test_oneOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_1`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:922:4
|
|
||||||
|
|
|
||||||
922 | fn test_oneOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_2`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:928:4
|
|
||||||
|
|
|
||||||
928 | fn test_oneOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_3`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:934:4
|
|
||||||
|
|
|
||||||
934 | fn test_oneOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_4`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:940:4
|
|
||||||
|
|
|
||||||
940 | fn test_oneOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_5`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:946:4
|
|
||||||
|
|
|
||||||
946 | fn test_oneOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_6`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:952:4
|
|
||||||
|
|
|
||||||
952 | fn test_oneOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_7`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:958:4
|
|
||||||
|
|
|
||||||
958 | fn test_oneOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_8`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:964:4
|
|
||||||
|
|
|
||||||
964 | fn test_oneOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_9`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:970:4
|
|
||||||
|
|
|
||||||
970 | fn test_oneOf_10() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_10`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:976:4
|
|
||||||
|
|
|
||||||
976 | fn test_oneOf_11() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_11`
|
|
||||||
|
|
||||||
warning: function `test_oneOf_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:982:4
|
|
||||||
|
|
|
||||||
982 | fn test_oneOf_12() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_one_of_12`
|
|
||||||
|
|
||||||
warning: function `test_emptyString_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1072:4
|
|
||||||
|
|
|
||||||
1072 | fn test_emptyString_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_empty_string_0`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1090:4
|
|
||||||
|
|
|
||||||
1090 | fn test_maxProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1096:4
|
|
||||||
|
|
|
||||||
1096 | fn test_maxProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1102:4
|
|
||||||
|
|
|
||||||
1102 | fn test_maxProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_maxProperties_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1108:4
|
|
||||||
|
|
|
||||||
1108 | fn test_maxProperties_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_properties_3`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1114:4
|
|
||||||
|
|
|
||||||
1114 | fn test_dependentRequired_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_0`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1120:4
|
|
||||||
|
|
|
||||||
1120 | fn test_dependentRequired_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_1`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1126:4
|
|
||||||
|
|
|
||||||
1126 | fn test_dependentRequired_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_2`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1132:4
|
|
||||||
|
|
|
||||||
1132 | fn test_dependentRequired_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_3`
|
|
||||||
|
|
||||||
warning: function `test_dependentRequired_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1138:4
|
|
||||||
|
|
|
||||||
1138 | fn test_dependentRequired_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dependent_required_4`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1252:4
|
|
||||||
|
|
|
||||||
1252 | fn test_multipleOf_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_0`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1258:4
|
|
||||||
|
|
|
||||||
1258 | fn test_multipleOf_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_1`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1264:4
|
|
||||||
|
|
|
||||||
1264 | fn test_multipleOf_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_2`
|
|
||||||
|
|
||||||
warning: function `test_multipleOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1270:4
|
|
||||||
|
|
|
||||||
1270 | fn test_multipleOf_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_multiple_of_3`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1276:4
|
|
||||||
|
|
|
||||||
1276 | fn test_patternProperties_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_0`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1282:4
|
|
||||||
|
|
|
||||||
1282 | fn test_patternProperties_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_1`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1288:4
|
|
||||||
|
|
|
||||||
1288 | fn test_patternProperties_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_2`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1294:4
|
|
||||||
|
|
|
||||||
1294 | fn test_patternProperties_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_3`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1300:4
|
|
||||||
|
|
|
||||||
1300 | fn test_patternProperties_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_4`
|
|
||||||
|
|
||||||
warning: function `test_patternProperties_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1306:4
|
|
||||||
|
|
|
||||||
1306 | fn test_patternProperties_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_pattern_properties_5`
|
|
||||||
|
|
||||||
warning: function `test_allOf_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1336:4
|
|
||||||
|
|
|
||||||
1336 | fn test_allOf_0() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_0`
|
|
||||||
|
|
||||||
warning: function `test_allOf_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1342:4
|
|
||||||
|
|
|
||||||
1342 | fn test_allOf_1() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_1`
|
|
||||||
|
|
||||||
warning: function `test_allOf_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1348:4
|
|
||||||
|
|
|
||||||
1348 | fn test_allOf_2() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_2`
|
|
||||||
|
|
||||||
warning: function `test_allOf_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1354:4
|
|
||||||
|
|
|
||||||
1354 | fn test_allOf_3() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_3`
|
|
||||||
|
|
||||||
warning: function `test_allOf_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1360:4
|
|
||||||
|
|
|
||||||
1360 | fn test_allOf_4() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_4`
|
|
||||||
|
|
||||||
warning: function `test_allOf_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1366:4
|
|
||||||
|
|
|
||||||
1366 | fn test_allOf_5() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_5`
|
|
||||||
|
|
||||||
warning: function `test_allOf_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:1372:4
|
|
||||||
|
|
|
||||||
1372 | fn test_allOf_6() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_6`
|
|
||||||
|
|
||||||
warning: function `test_allOf_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:1378:4
|
|
||||||
|
|
|
||||||
1378 | fn test_allOf_7() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_7`
|
|
||||||
|
|
||||||
warning: function `test_allOf_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:1384:4
|
|
||||||
|
|
|
||||||
1384 | fn test_allOf_8() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_8`
|
|
||||||
|
|
||||||
warning: function `test_allOf_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:1390:4
|
|
||||||
|
|
|
||||||
1390 | fn test_allOf_9() {
|
|
||||||
| ^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_9`
|
|
||||||
|
|
||||||
warning: function `test_allOf_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:1396:4
|
|
||||||
|
|
|
||||||
1396 | fn test_allOf_10() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_10`
|
|
||||||
|
|
||||||
warning: function `test_allOf_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:1402:4
|
|
||||||
|
|
|
||||||
1402 | fn test_allOf_11() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_11`
|
|
||||||
|
|
||||||
warning: function `test_allOf_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:1408:4
|
|
||||||
|
|
|
||||||
1408 | fn test_allOf_12() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_12`
|
|
||||||
|
|
||||||
warning: function `test_allOf_13` should have a snake case name
|
|
||||||
--> tests/tests.rs:1414:4
|
|
||||||
|
|
|
||||||
1414 | fn test_allOf_13() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_13`
|
|
||||||
|
|
||||||
warning: function `test_allOf_14` should have a snake case name
|
|
||||||
--> tests/tests.rs:1420:4
|
|
||||||
|
|
|
||||||
1420 | fn test_allOf_14() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_14`
|
|
||||||
|
|
||||||
warning: function `test_allOf_15` should have a snake case name
|
|
||||||
--> tests/tests.rs:1426:4
|
|
||||||
|
|
|
||||||
1426 | fn test_allOf_15() {
|
|
||||||
| ^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_all_of_15`
|
|
||||||
|
|
||||||
warning: function `test_minLength_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1828:4
|
|
||||||
|
|
|
||||||
1828 | fn test_minLength_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_length_0`
|
|
||||||
|
|
||||||
warning: function `test_minLength_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1834:4
|
|
||||||
|
|
|
||||||
1834 | fn test_minLength_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_min_length_1`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1840:4
|
|
||||||
|
|
|
||||||
1840 | fn test_maxItems_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_0`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1846:4
|
|
||||||
|
|
|
||||||
1846 | fn test_maxItems_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_1`
|
|
||||||
|
|
||||||
warning: function `test_maxItems_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1852:4
|
|
||||||
|
|
|
||||||
1852 | fn test_maxItems_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_max_items_2`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_0` should have a snake case name
|
|
||||||
--> tests/tests.rs:1912:4
|
|
||||||
|
|
|
||||||
1912 | fn test_dynamicRef_0() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_0`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_1` should have a snake case name
|
|
||||||
--> tests/tests.rs:1918:4
|
|
||||||
|
|
|
||||||
1918 | fn test_dynamicRef_1() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_1`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_2` should have a snake case name
|
|
||||||
--> tests/tests.rs:1924:4
|
|
||||||
|
|
|
||||||
1924 | fn test_dynamicRef_2() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_2`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_3` should have a snake case name
|
|
||||||
--> tests/tests.rs:1930:4
|
|
||||||
|
|
|
||||||
1930 | fn test_dynamicRef_3() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_3`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_4` should have a snake case name
|
|
||||||
--> tests/tests.rs:1936:4
|
|
||||||
|
|
|
||||||
1936 | fn test_dynamicRef_4() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_4`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_5` should have a snake case name
|
|
||||||
--> tests/tests.rs:1942:4
|
|
||||||
|
|
|
||||||
1942 | fn test_dynamicRef_5() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_5`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_6` should have a snake case name
|
|
||||||
--> tests/tests.rs:1948:4
|
|
||||||
|
|
|
||||||
1948 | fn test_dynamicRef_6() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_6`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_7` should have a snake case name
|
|
||||||
--> tests/tests.rs:1954:4
|
|
||||||
|
|
|
||||||
1954 | fn test_dynamicRef_7() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_7`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_8` should have a snake case name
|
|
||||||
--> tests/tests.rs:1960:4
|
|
||||||
|
|
|
||||||
1960 | fn test_dynamicRef_8() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_8`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_9` should have a snake case name
|
|
||||||
--> tests/tests.rs:1966:4
|
|
||||||
|
|
|
||||||
1966 | fn test_dynamicRef_9() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_9`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_10` should have a snake case name
|
|
||||||
--> tests/tests.rs:1972:4
|
|
||||||
|
|
|
||||||
1972 | fn test_dynamicRef_10() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_10`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_11` should have a snake case name
|
|
||||||
--> tests/tests.rs:1978:4
|
|
||||||
|
|
|
||||||
1978 | fn test_dynamicRef_11() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_11`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_12` should have a snake case name
|
|
||||||
--> tests/tests.rs:1984:4
|
|
||||||
|
|
|
||||||
1984 | fn test_dynamicRef_12() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_12`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_13` should have a snake case name
|
|
||||||
--> tests/tests.rs:1990:4
|
|
||||||
|
|
|
||||||
1990 | fn test_dynamicRef_13() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_13`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_14` should have a snake case name
|
|
||||||
--> tests/tests.rs:1996:4
|
|
||||||
|
|
|
||||||
1996 | fn test_dynamicRef_14() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_14`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_15` should have a snake case name
|
|
||||||
--> tests/tests.rs:2002:4
|
|
||||||
|
|
|
||||||
2002 | fn test_dynamicRef_15() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_15`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_16` should have a snake case name
|
|
||||||
--> tests/tests.rs:2008:4
|
|
||||||
|
|
|
||||||
2008 | fn test_dynamicRef_16() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_16`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_17` should have a snake case name
|
|
||||||
--> tests/tests.rs:2014:4
|
|
||||||
|
|
|
||||||
2014 | fn test_dynamicRef_17() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_17`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_18` should have a snake case name
|
|
||||||
--> tests/tests.rs:2020:4
|
|
||||||
|
|
|
||||||
2020 | fn test_dynamicRef_18() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_18`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_19` should have a snake case name
|
|
||||||
--> tests/tests.rs:2026:4
|
|
||||||
|
|
|
||||||
2026 | fn test_dynamicRef_19() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_19`
|
|
||||||
|
|
||||||
warning: function `test_dynamicRef_20` should have a snake case name
|
|
||||||
--> tests/tests.rs:2032:4
|
|
||||||
|
|
|
||||||
2032 | fn test_dynamicRef_20() {
|
|
||||||
| ^^^^^^^^^^^^^^^^^^ help: convert the identifier to snake case: `test_dynamic_ref_20`
|
|
||||||
|
|
||||||
warning: `jspg` (test "tests") generated 132 warnings
|
|
||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 6.12s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
|
|
||||||
thread 'test_ref_39' (14867888) panicked at tests/tests.rs:1812:45:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[implicit keyword shadowing] Test 'child type overrides parent type' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'age'\", details: ErrorDetails { path: \"/age\" } }]\n[implicit keyword shadowing] Test 'parent max age (20) is shadowed (replaced) by child definition' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'age'\", details: ErrorDetails { path: \"/age\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_ref_39 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_ref_39
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.00s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
106
log_root.txt
106
log_root.txt
@ -1,106 +0,0 @@
|
|||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.34s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"job_id", "manager_id", "name"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"manager_id", "type", "job_id", "name"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"name", "job_id", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "name", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"name", "job_id", "manager_id", "nested_or_super_job", "type"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"root_job", "name", "job_id", "manager_id", "nested_or_super_job", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"name", "manager_id", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"manager_id", "name", "type", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"manager_id", "type", "job_id", "name"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"manager_id", "type", "name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"manager_id", "type", "nested_or_super_job", "name", "job_id"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"manager_id", "root_job", "type", "nested_or_super_job", "name", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/my_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/my_job/type. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"type", "name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: validate_object inserted 'my_job' at /nested_or_super_job/my_job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"job_id", "manager_id", "name"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "type", "manager_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
|
|
||||||
thread 'test_puncs_6' (15117383) panicked at tests/tests.rs:150:44:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[complex punc type matching with oneOf and nested refs] Test 'valid person against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'first_name'\", details: ErrorDetails { path: \"/first_name\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against strict punc' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_puncs_6 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_puncs_6
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.00s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
243
old_code/lib.rs
243
old_code/lib.rs
@ -1,243 +0,0 @@
|
|||||||
use pgrx::*;
|
|
||||||
|
|
||||||
pg_module_magic!();
|
|
||||||
|
|
||||||
// mod schema;
|
|
||||||
mod registry;
|
|
||||||
mod validator;
|
|
||||||
mod util;
|
|
||||||
|
|
||||||
use crate::registry::REGISTRY;
|
|
||||||
// use crate::schema::Schema;
|
|
||||||
use crate::validator::{Validator, ValidationOptions};
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
||||||
enum SchemaType {
|
|
||||||
Enum,
|
|
||||||
Type,
|
|
||||||
Family,
|
|
||||||
PublicPunc,
|
|
||||||
PrivatePunc,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CachedSchema {
|
|
||||||
t: SchemaType,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref SCHEMA_META: std::sync::RwLock<HashMap<String, CachedSchema>> = std::sync::RwLock::new(HashMap::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern(strict)]
|
|
||||||
fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
|
||||||
let mut meta = SCHEMA_META.write().unwrap();
|
|
||||||
let enums_value: Value = enums.0;
|
|
||||||
let types_value: Value = types.0;
|
|
||||||
let puncs_value: Value = puncs.0;
|
|
||||||
|
|
||||||
let mut schemas_to_register = Vec::new();
|
|
||||||
|
|
||||||
// Phase 1: Enums
|
|
||||||
if let Some(enums_array) = enums_value.as_array() {
|
|
||||||
for enum_row in enums_array {
|
|
||||||
if let Some(schemas_raw) = enum_row.get("schemas") {
|
|
||||||
if let Some(schemas_array) = schemas_raw.as_array() {
|
|
||||||
for schema_def in schemas_array {
|
|
||||||
if let Some(schema_id) = schema_def.get("$id").and_then(|v| v.as_str()) {
|
|
||||||
schemas_to_register.push((schema_id.to_string(), schema_def.clone(), SchemaType::Enum));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 2: Types & Hierarchy
|
|
||||||
let mut hierarchy_map: HashMap<String, HashSet<String>> = HashMap::new();
|
|
||||||
if let Some(types_array) = types_value.as_array() {
|
|
||||||
for type_row in types_array {
|
|
||||||
if let Some(schemas_raw) = type_row.get("schemas") {
|
|
||||||
if let Some(schemas_array) = schemas_raw.as_array() {
|
|
||||||
for schema_def in schemas_array {
|
|
||||||
if let Some(schema_id) = schema_def.get("$id").and_then(|v| v.as_str()) {
|
|
||||||
schemas_to_register.push((schema_id.to_string(), schema_def.clone(), SchemaType::Type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(type_name) = type_row.get("name").and_then(|v| v.as_str()) {
|
|
||||||
if let Some(hierarchy_raw) = type_row.get("hierarchy") {
|
|
||||||
if let Some(hierarchy_array) = hierarchy_raw.as_array() {
|
|
||||||
for ancestor_val in hierarchy_array {
|
|
||||||
if let Some(ancestor_name) = ancestor_val.as_str() {
|
|
||||||
hierarchy_map.entry(ancestor_name.to_string()).or_default().insert(type_name.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (base_type, descendant_types) in hierarchy_map {
|
|
||||||
let family_id = format!("{}.family", base_type);
|
|
||||||
let values: Vec<String> = descendant_types.into_iter().collect();
|
|
||||||
let family_schema = json!({ "$id": family_id, "type": "string", "enum": values });
|
|
||||||
schemas_to_register.push((family_id, family_schema, SchemaType::Family));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Phase 3: Puncs
|
|
||||||
if let Some(puncs_array) = puncs_value.as_array() {
|
|
||||||
for punc_row in puncs_array {
|
|
||||||
if let Some(punc_obj) = punc_row.as_object() {
|
|
||||||
if let Some(punc_name) = punc_obj.get("name").and_then(|v| v.as_str()) {
|
|
||||||
let is_public = punc_obj.get("public").and_then(|v| v.as_bool()).unwrap_or(false);
|
|
||||||
let punc_type = if is_public { SchemaType::PublicPunc } else { SchemaType::PrivatePunc };
|
|
||||||
if let Some(schemas_raw) = punc_obj.get("schemas") {
|
|
||||||
if let Some(schemas_array) = schemas_raw.as_array() {
|
|
||||||
for schema_def in schemas_array {
|
|
||||||
if let Some(schema_id) = schema_def.get("$id").and_then(|v| v.as_str()) {
|
|
||||||
let req_id = format!("{}.request", punc_name);
|
|
||||||
let resp_id = format!("{}.response", punc_name);
|
|
||||||
let st = if schema_id == req_id || schema_id == resp_id { punc_type } else { SchemaType::Type };
|
|
||||||
schemas_to_register.push((schema_id.to_string(), schema_def.clone(), st));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut all_errors = Vec::new();
|
|
||||||
for (id, value, st) in schemas_to_register {
|
|
||||||
// Meta-validation: Check 'type' enum if present
|
|
||||||
if let Some(type_val) = value.get("type") {
|
|
||||||
let types = match type_val {
|
|
||||||
Value::String(s) => vec![s.as_str()],
|
|
||||||
Value::Array(a) => a.iter().filter_map(|v| v.as_str()).collect(),
|
|
||||||
_ => vec![],
|
|
||||||
};
|
|
||||||
let valid_primitives = ["string", "number", "integer", "boolean", "array", "object", "null"];
|
|
||||||
for t in types {
|
|
||||||
if !valid_primitives.contains(&t) {
|
|
||||||
all_errors.push(json!({ "code": "ENUM_VIOLATED", "message": format!("Invalid type: {}", t) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone value for insertion since it might be consumed/moved if we were doing other things
|
|
||||||
let value_for_registry = value.clone();
|
|
||||||
|
|
||||||
// Validation: just ensure it is an object or boolean
|
|
||||||
if value.is_object() || value.is_boolean() {
|
|
||||||
REGISTRY.insert(id.clone(), value_for_registry);
|
|
||||||
meta.insert(id, CachedSchema { t: st });
|
|
||||||
} else {
|
|
||||||
all_errors.push(json!({ "code": "INVALID_SCHEMA_TYPE", "message": format!("Schema {} must be an object or boolean", id) }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !all_errors.is_empty() {
|
|
||||||
return JsonB(json!({ "errors": all_errors }));
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonB(json!({ "response": "success" }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
|
||||||
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
|
||||||
let schema = match REGISTRY.get(schema_id) {
|
|
||||||
Some(s) => s,
|
|
||||||
None => return JsonB(json!({
|
|
||||||
"errors": [{
|
|
||||||
"code": "SCHEMA_NOT_FOUND",
|
|
||||||
"message": format!("Schema '{}' not found", schema_id),
|
|
||||||
"details": { "schema": schema_id }
|
|
||||||
}]
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
let meta = SCHEMA_META.read().unwrap();
|
|
||||||
let st = meta.get(schema_id).map(|m| m.t).unwrap_or(SchemaType::Type);
|
|
||||||
|
|
||||||
let be_strict = match st {
|
|
||||||
SchemaType::PublicPunc => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
let options = ValidationOptions {
|
|
||||||
be_strict,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut validator = Validator::new(options, schema_id);
|
|
||||||
match validator.validate(&schema, &instance.0) {
|
|
||||||
Ok(_) => JsonB(json!({ "response": "success" })),
|
|
||||||
Err(errors) => {
|
|
||||||
let drop_errors: Vec<Value> = errors.into_iter().map(|e| json!({
|
|
||||||
"code": e.code,
|
|
||||||
"message": e.message,
|
|
||||||
"details": {
|
|
||||||
"path": e.path,
|
|
||||||
"context": e.context,
|
|
||||||
"cause": e.cause,
|
|
||||||
"schema": e.schema_id
|
|
||||||
}
|
|
||||||
})).collect();
|
|
||||||
|
|
||||||
if let Ok(mut f) = std::fs::OpenOptions::new().create(true).append(true).open("/tmp/debug_jspg_errors.log") {
|
|
||||||
use std::io::Write;
|
|
||||||
let _ = writeln!(f, "VALIDATION FAILED for {}: {:?}", schema_id, drop_errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonB(json!({ "errors": drop_errors }))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
|
||||||
fn json_schema_cached(schema_id: &str) -> bool {
|
|
||||||
REGISTRY.get(schema_id).is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern(strict)]
|
|
||||||
fn clear_json_schemas() -> JsonB {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let mut meta = SCHEMA_META.write().unwrap();
|
|
||||||
meta.clear();
|
|
||||||
JsonB(json!({ "response": "success" }))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
|
||||||
fn show_json_schemas() -> JsonB {
|
|
||||||
let meta = SCHEMA_META.read().unwrap();
|
|
||||||
let ids: Vec<String> = meta.keys().cloned().collect();
|
|
||||||
JsonB(json!({ "response": ids }))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This module is required by `cargo pgrx test` invocations.
|
|
||||||
/// It must be visible at the root of your extension crate.
|
|
||||||
#[cfg(test)]
|
|
||||||
pub mod pg_test {
|
|
||||||
pub fn setup(_options: Vec<&str>) {
|
|
||||||
// perform one-off initialization when the pg_test framework starts
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn postgresql_conf_options() -> Vec<&'static str> {
|
|
||||||
// return any postgresql.conf settings that are required for your tests
|
|
||||||
vec![]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "pg_test"))]
|
|
||||||
#[pg_schema]
|
|
||||||
mod tests {
|
|
||||||
use pgrx::pg_test;
|
|
||||||
include!("suite.rs");
|
|
||||||
}
|
|
||||||
@ -1,217 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::RwLock;
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
pub static ref REGISTRY: Registry = Registry::new();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Registry {
|
|
||||||
schemas: RwLock<HashMap<String, Value>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Registry {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
schemas: RwLock::new(HashMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&self) {
|
|
||||||
let mut schemas = self.schemas.write().unwrap();
|
|
||||||
schemas.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&self, id: String, schema: Value) {
|
|
||||||
let mut schemas = self.schemas.write().unwrap();
|
|
||||||
|
|
||||||
// Index the schema and its sub-resources (IDs and anchors)
|
|
||||||
self.index_schema(&schema, &mut schemas, Some(&id));
|
|
||||||
|
|
||||||
// Ensure the root ID is inserted (index_schema handles it, but let's be explicit)
|
|
||||||
schemas.insert(id, schema);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index_schema(&self, schema: &Value, registry: &mut HashMap<String, Value>, current_scope: Option<&str>) {
|
|
||||||
if let Value::Object(map) = schema {
|
|
||||||
// Only strictly index $id for scope resolution
|
|
||||||
let mut my_scope = current_scope.map(|s| s.to_string());
|
|
||||||
|
|
||||||
if let Some(Value::String(id)) = map.get("$id") {
|
|
||||||
if id.contains("://") {
|
|
||||||
my_scope = Some(id.clone());
|
|
||||||
} else if let Some(scope) = current_scope {
|
|
||||||
if let Some(pos) = scope.rfind('/') {
|
|
||||||
my_scope = Some(format!("{}{}", &scope[..pos + 1], id));
|
|
||||||
} else {
|
|
||||||
my_scope = Some(id.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
my_scope = Some(id.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(final_id) = &my_scope {
|
|
||||||
registry.insert(final_id.clone(), schema.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Minimal recursion only for definitions where sub-IDs often live
|
|
||||||
// This is a tradeoff: we don't index EVERYWHERE, but we catch the 90% common case of
|
|
||||||
// bundled definitions without full tree traversal.
|
|
||||||
if let Some(Value::Object(defs)) = map.get("$defs").or_else(|| map.get("definitions")) {
|
|
||||||
for (_, def_schema) in defs {
|
|
||||||
self.index_schema(def_schema, registry, my_scope.as_deref());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get(&self, id: &str) -> Option<Value> {
|
|
||||||
let schemas = self.schemas.read().unwrap();
|
|
||||||
schemas.get(id).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resolve(&self, ref_str: &str, current_id: Option<&str>) -> Option<(Value, String)> {
|
|
||||||
// 1. Try full lookup (Absolute or explicit ID)
|
|
||||||
if let Some(s) = self.get(ref_str) {
|
|
||||||
return Some((s, ref_str.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Try Relative lookup against current scope
|
|
||||||
if let Some(curr) = current_id {
|
|
||||||
if let Some(pos) = curr.rfind('/') {
|
|
||||||
let joined = format!("{}{}", &curr[..pos + 1], ref_str);
|
|
||||||
if let Some(s) = self.get(&joined) {
|
|
||||||
return Some((s, joined));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Pointer Resolution
|
|
||||||
// Split into Base URI + Fragment
|
|
||||||
let (base, fragment) = match ref_str.split_once('#') {
|
|
||||||
Some((b, f)) => (b, Some(f)),
|
|
||||||
None => (ref_str, None),
|
|
||||||
};
|
|
||||||
|
|
||||||
// If base is empty, we stay in current schema.
|
|
||||||
// If base is present, we resolve it first.
|
|
||||||
let (root_schema, scope) = if base.is_empty() {
|
|
||||||
if let Some(curr) = current_id {
|
|
||||||
// If we are looking up internally, we rely on the caller having passed the correct current ID
|
|
||||||
// But typically internal refs are just fragments.
|
|
||||||
if let Some(s) = self.get(curr) {
|
|
||||||
(s, curr.to_string())
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Resolve external base
|
|
||||||
if let Some(s) = self.get(base) {
|
|
||||||
(s, base.to_string())
|
|
||||||
} else if let Some(curr) = current_id {
|
|
||||||
// Try relative base
|
|
||||||
if let Some(pos) = curr.rfind('/') {
|
|
||||||
let joined = format!("{}{}", &curr[..pos + 1], base);
|
|
||||||
if let Some(s) = self.get(&joined) {
|
|
||||||
(s, joined)
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(frag_raw) = fragment {
|
|
||||||
if frag_raw.is_empty() {
|
|
||||||
return Some((root_schema, scope));
|
|
||||||
}
|
|
||||||
// Decode fragment (it is URI encoded)
|
|
||||||
let frag_cow = percent_encoding::percent_decode_str(frag_raw).decode_utf8().unwrap_or(std::borrow::Cow::Borrowed(frag_raw));
|
|
||||||
let frag = frag_cow.as_ref();
|
|
||||||
|
|
||||||
if frag.starts_with('/') {
|
|
||||||
if let Some(sub) = root_schema.pointer(frag) {
|
|
||||||
return Some((sub.clone(), scope));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// It is an anchor. We scan for it at runtime to avoid complex indexing at insertion.
|
|
||||||
if let Some(sub) = self.find_anchor(&root_schema, frag) {
|
|
||||||
return Some((sub, scope));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some((root_schema, scope))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_anchor(&self, schema: &Value, anchor: &str) -> Option<Value> {
|
|
||||||
match schema {
|
|
||||||
Value::Object(map) => {
|
|
||||||
// Check if this schema itself has the anchor
|
|
||||||
if let Some(Value::String(a)) = map.get("$anchor") {
|
|
||||||
if a == anchor {
|
|
||||||
return Some(schema.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into $defs / definitions (Map of Schemas)
|
|
||||||
if let Some(Value::Object(defs)) = map.get("$defs").or_else(|| map.get("definitions")) {
|
|
||||||
for val in defs.values() {
|
|
||||||
if let Some(found) = self.find_anchor(val, anchor) { return Some(found); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into properties / patternProperties / dependentSchemas (Map of Schemas)
|
|
||||||
for key in ["properties", "patternProperties", "dependentSchemas"] {
|
|
||||||
if let Some(Value::Object(props)) = map.get(key) {
|
|
||||||
for val in props.values() {
|
|
||||||
if let Some(found) = self.find_anchor(val, anchor) { return Some(found); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into arrays of schemas
|
|
||||||
for key in ["allOf", "anyOf", "oneOf", "prefixItems"] {
|
|
||||||
if let Some(Value::Array(arr)) = map.get(key) {
|
|
||||||
for item in arr {
|
|
||||||
if let Some(found) = self.find_anchor(item, anchor) { return Some(found); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recurse into single sub-schemas
|
|
||||||
for key in ["items", "contains", "additionalProperties", "unevaluatedProperties", "not", "if", "then", "else"] {
|
|
||||||
if let Some(val) = map.get(key) {
|
|
||||||
if val.is_object() || val.is_boolean() {
|
|
||||||
if let Some(found) = self.find_anchor(val, anchor) { return Some(found); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
Value::Array(arr) => {
|
|
||||||
// Should not happen for a schema object, but if we are passed an array of schemas?
|
|
||||||
// Standard schema is object or bool.
|
|
||||||
// But let's be safe.
|
|
||||||
for item in arr {
|
|
||||||
if let Some(found) = self.find_anchor(item, anchor) {
|
|
||||||
return Some(found);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ -1,236 +0,0 @@
|
|||||||
// use crate::schema::Schema;
|
|
||||||
use crate::registry::REGISTRY;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use pgrx::JsonB;
|
|
||||||
use std::{fs, path::Path};
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct ExpectedError {
|
|
||||||
code: String,
|
|
||||||
path: String,
|
|
||||||
message_contains: Option<String>,
|
|
||||||
cause: Option<Value>,
|
|
||||||
context: Option<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Group {
|
|
||||||
description: String,
|
|
||||||
schema: Option<Value>,
|
|
||||||
enums: Option<Value>,
|
|
||||||
types: Option<Value>,
|
|
||||||
puncs: Option<Value>,
|
|
||||||
tests: Vec<TestCase>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct TestCase {
|
|
||||||
description: String,
|
|
||||||
data: Value,
|
|
||||||
valid: bool,
|
|
||||||
action: Option<String>,
|
|
||||||
schema_id: Option<String>,
|
|
||||||
expect_errors: Option<Vec<ExpectedError>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
include!("tests.rs");
|
|
||||||
|
|
||||||
fn load_remotes(dir: &Path, base_url: &str) {
|
|
||||||
if !dir.exists() { return; }
|
|
||||||
|
|
||||||
for entry in fs::read_dir(dir).expect("Failed to read remotes directory") {
|
|
||||||
let entry = entry.unwrap();
|
|
||||||
let path = entry.path();
|
|
||||||
let file_name = path.file_name().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
if path.is_file() && file_name.ends_with(".json") {
|
|
||||||
let content = fs::read_to_string(&path).expect("Failed to read remote file");
|
|
||||||
if let Ok(schema_value) = serde_json::from_str::<Value>(&content) {
|
|
||||||
// Just check if it's a valid JSON value for a schema (object or bool)
|
|
||||||
if schema_value.is_object() || schema_value.is_boolean() {
|
|
||||||
let schema_id = format!("{}{}", base_url, file_name);
|
|
||||||
REGISTRY.insert(schema_id, schema_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if path.is_dir() {
|
|
||||||
load_remotes(&path, &format!("{}{}/", base_url, file_name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock the meta-schema for testing recursive refs
|
|
||||||
let meta_id = "https://json-schema.org/draft/2020-12/schema";
|
|
||||||
if REGISTRY.get(meta_id).is_none() {
|
|
||||||
// Just mock it as a permissive schema for now so refs resolve
|
|
||||||
REGISTRY.insert(meta_id.to_string(), json!({ "$id": meta_id }));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn run_dir(dir: &Path, base_url: Option<&str>) -> (usize, usize) {
|
|
||||||
let mut file_count = 0;
|
|
||||||
let mut test_count = 0;
|
|
||||||
|
|
||||||
for entry in fs::read_dir(dir).expect("Failed to read directory") {
|
|
||||||
let entry = entry.unwrap();
|
|
||||||
let path = entry.path();
|
|
||||||
let file_name = path.file_name().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
if path.is_file() && file_name.ends_with(".json") {
|
|
||||||
let count = run_file(&path, base_url);
|
|
||||||
test_count += count;
|
|
||||||
file_count += 1;
|
|
||||||
} else if path.is_dir() {
|
|
||||||
if !file_name.starts_with('.') && file_name != "optional" {
|
|
||||||
let (f, t) = run_dir(&path, base_url);
|
|
||||||
file_count += f;
|
|
||||||
test_count += t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(file_count, test_count)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_file(path: &Path, base_url: Option<&str>) -> usize {
|
|
||||||
let content = fs::read_to_string(path).expect("Failed to read file");
|
|
||||||
let groups: Vec<Group> = serde_json::from_str(&content).expect("Failed to parse JSON");
|
|
||||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
|
||||||
|
|
||||||
let mut test_count = 0;
|
|
||||||
|
|
||||||
for group in groups {
|
|
||||||
// Handle JSPG setup if any JSPG fields are present
|
|
||||||
if group.enums.is_some() || group.types.is_some() || group.puncs.is_some() {
|
|
||||||
let enums = group.enums.clone().unwrap_or(json!([]));
|
|
||||||
let types = group.types.clone().unwrap_or(json!([]));
|
|
||||||
let puncs = group.puncs.clone().unwrap_or(json!([]));
|
|
||||||
// Use internal helper to register without clearing
|
|
||||||
let result = crate::cache_json_schemas(JsonB(enums), JsonB(types), JsonB(puncs));
|
|
||||||
if let Some(errors) = result.0.get("errors") {
|
|
||||||
// If the group has a test specifically for caching failures, don't panic here
|
|
||||||
let has_cache_test = group.tests.iter().any(|t| t.action.as_deref() == Some("cache"));
|
|
||||||
if !has_cache_test {
|
|
||||||
panic!("FAILED: File: {}, Group: {}\nCache failed: {:?}", filename, group.description, errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut temp_id = "test_root".to_string();
|
|
||||||
if let Some(schema_value) = &group.schema {
|
|
||||||
temp_id = base_url.map(|b| format!("{}schema.json", b)).unwrap_or_else(|| "test_root".to_string());
|
|
||||||
|
|
||||||
if schema_value.is_object() || schema_value.is_boolean() {
|
|
||||||
REGISTRY.insert(temp_id.clone(), schema_value.clone());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Fallback for JSPG style tests where the schema is in the puncs/types
|
|
||||||
let get_first_id = |items: &Option<Value>| {
|
|
||||||
items.as_ref()
|
|
||||||
.and_then(|v| v.as_array())
|
|
||||||
.and_then(|arr| arr.first())
|
|
||||||
.and_then(|item| item.get("schemas"))
|
|
||||||
.and_then(|v| v.as_array())
|
|
||||||
.and_then(|arr| arr.first())
|
|
||||||
.and_then(|sch| sch.get("$id"))
|
|
||||||
.and_then(|id| id.as_str())
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(id) = get_first_id(&group.puncs).or_else(|| get_first_id(&group.types)) {
|
|
||||||
temp_id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for test in &group.tests {
|
|
||||||
test_count += 1;
|
|
||||||
let sid = test.schema_id.clone().unwrap_or_else(|| temp_id.clone());
|
|
||||||
let action = test.action.as_deref().unwrap_or("validate");
|
|
||||||
pgrx::notice!("Starting Test: {}", test.description);
|
|
||||||
|
|
||||||
let result = if action == "cache" {
|
|
||||||
let enums = group.enums.clone().unwrap_or(json!([]));
|
|
||||||
let types = group.types.clone().unwrap_or(json!([]));
|
|
||||||
let puncs = group.puncs.clone().unwrap_or(json!([]));
|
|
||||||
crate::cache_json_schemas(JsonB(enums), JsonB(types), JsonB(puncs))
|
|
||||||
} else {
|
|
||||||
crate::validate_json_schema(&sid, JsonB(test.data.clone()))
|
|
||||||
};
|
|
||||||
let is_success = result.0.get("response").is_some();
|
|
||||||
pgrx::notice!("TEST: file={}, group={}, test={}, valid={}, outcome={}",
|
|
||||||
filename,
|
|
||||||
&group.description,
|
|
||||||
&test.description,
|
|
||||||
test.valid,
|
|
||||||
if is_success { "SUCCESS" } else { "ERRORS" }
|
|
||||||
);
|
|
||||||
|
|
||||||
if is_success != test.valid {
|
|
||||||
if let Some(errs) = result.0.get("errors") {
|
|
||||||
panic!(
|
|
||||||
"FAILED: File: {}, Group: {}, Test: {}\nExpected valid: {}, got ERRORS: {:?}",
|
|
||||||
filename,
|
|
||||||
group.description,
|
|
||||||
test.description,
|
|
||||||
test.valid,
|
|
||||||
errs
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"FAILED: File: {}, Group: {}, Test: {}\nExpected invalid, got SUCCESS",
|
|
||||||
filename,
|
|
||||||
group.description,
|
|
||||||
test.description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform detailed assertions if present
|
|
||||||
if let Some(expectations) = &test.expect_errors {
|
|
||||||
let actual_errors = result.0.get("errors").and_then(|e| e.as_array()).expect("Expected errors array in failure response");
|
|
||||||
|
|
||||||
for expected in expectations {
|
|
||||||
let found = actual_errors.iter().any(|e| {
|
|
||||||
let code = e["code"].as_str().unwrap_or("");
|
|
||||||
let path = e["details"]["path"].as_str().unwrap_or("");
|
|
||||||
let message = e["message"].as_str().unwrap_or("");
|
|
||||||
|
|
||||||
let code_match = code == expected.code;
|
|
||||||
let path_match = path == expected.path;
|
|
||||||
let msg_match = if let Some(sub) = &expected.message_contains {
|
|
||||||
message.contains(sub)
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let matches_cause = if let Some(expected_cause) = &expected.cause {
|
|
||||||
e["details"]["cause"] == *expected_cause
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let matches_context = if let Some(expected_context) = &expected.context {
|
|
||||||
e["details"]["context"] == *expected_context
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
code_match && path_match && msg_match && matches_cause && matches_context
|
|
||||||
});
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
panic!(
|
|
||||||
"FAILED: File: {}, Group: {}, Test: {}\nMissing expected error: code='{}', path='{}'\nActual errors: {:?}",
|
|
||||||
filename,
|
|
||||||
group.description,
|
|
||||||
test.description,
|
|
||||||
expected.code,
|
|
||||||
expected.path,
|
|
||||||
actual_errors
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end of test loop
|
|
||||||
} // end of group loop
|
|
||||||
test_count
|
|
||||||
}
|
|
||||||
@ -1,482 +0,0 @@
|
|||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_additional_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/additionalProperties.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_cache() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/cache.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_const() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/const.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_dependencies() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/dependencies.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_enum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/enum.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_errors() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/errors.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_format() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/format.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_infinite_loop_detection() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/infinite-loop-detection.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_one_of() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/oneOf.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/properties.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_punc() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/punc.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_ref() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/ref.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_required() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/required.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_simple() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/simple.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_strict() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/strict.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_title() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/title.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_type() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/type.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_unevaluated_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/unevaluatedProperties.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_jspg_unique_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSPG-Test-Suite/uniqueItems.json"), None);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_additional_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/additionalProperties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_all_of() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/allOf.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_anchor() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/anchor.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_any_of() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/anyOf.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_boolean_schema() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/boolean_schema.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_const() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/const.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_contains() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/contains.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_content() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/content.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_default() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/default.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_defs() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/defs.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_dependent_required() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/dependentRequired.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_dependent_schemas() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/dependentSchemas.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_dynamic_ref() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/dynamicRef.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_enum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/enum.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_exclusive_maximum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/exclusiveMaximum.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_exclusive_minimum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/exclusiveMinimum.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_format() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/format.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_if_then_else() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/if-then-else.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_infinite_loop_detection() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/infinite-loop-detection.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/items.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_max_contains() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/maxContains.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_max_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/maxItems.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_max_length() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/maxLength.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_max_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/maxProperties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_maximum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/maximum.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_min_contains() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/minContains.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_min_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/minItems.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_min_length() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/minLength.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_min_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/minProperties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_minimum() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/minimum.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_multiple_of() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/multipleOf.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_not() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/not.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_one_of() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/oneOf.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_pattern() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/pattern.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_pattern_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/patternProperties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_prefix_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/prefixItems.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/properties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_property_names() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/propertyNames.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_ref() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/ref.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_ref_remote() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/refRemote.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_required() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/required.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_type() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/type.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_unevaluated_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/unevaluatedItems.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_unevaluated_properties() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/unevaluatedProperties.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_unique_items() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/uniqueItems.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pg_test]
|
|
||||||
fn test_json_schema_vocabulary() {
|
|
||||||
REGISTRY.reset();
|
|
||||||
let remotes_dir = Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/remotes");
|
|
||||||
load_remotes(remotes_dir, "http://localhost:1234/");
|
|
||||||
run_file(Path::new("/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg/tests/fixtures/JSON-Schema-Test-Suite/tests/draft2020-12/vocabulary.json"), Some("http://localhost:1234/"));
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
|
|
||||||
/// serde_json treats 0 and 0.0 not equal. so we cannot simply use v1==v2
|
|
||||||
pub fn equals(v1: &Value, v2: &Value) -> bool {
|
|
||||||
match (v1, v2) {
|
|
||||||
(Value::Null, Value::Null) => true,
|
|
||||||
(Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
|
|
||||||
(Value::Number(n1), Value::Number(n2)) => {
|
|
||||||
if let (Some(n1), Some(n2)) = (n1.as_u64(), n2.as_u64()) {
|
|
||||||
return n1 == n2;
|
|
||||||
}
|
|
||||||
if let (Some(n1), Some(n2)) = (n1.as_i64(), n2.as_i64()) {
|
|
||||||
return n1 == n2;
|
|
||||||
}
|
|
||||||
if let (Some(n1), Some(n2)) = (n1.as_f64(), n2.as_f64()) {
|
|
||||||
return (n1 - n2).abs() < f64::EPSILON;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
(Value::String(s1), Value::String(s2)) => s1 == s2,
|
|
||||||
(Value::Array(arr1), Value::Array(arr2)) => {
|
|
||||||
if arr1.len() != arr2.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
arr1.iter().zip(arr2).all(|(e1, e2)| equals(e1, e2))
|
|
||||||
}
|
|
||||||
(Value::Object(obj1), Value::Object(obj2)) => {
|
|
||||||
if obj1.len() != obj2.len() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (k1, v1) in obj1 {
|
|
||||||
if let Some(v2) = obj2.get(k1) {
|
|
||||||
if !equals(v1, v2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_integer(v: &Value) -> bool {
|
|
||||||
match v {
|
|
||||||
Value::Number(n) => {
|
|
||||||
n.is_i64() || n.is_u64() || n.as_f64().filter(|n| n.fract() == 0.0).is_some()
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,621 +0,0 @@
|
|||||||
use crate::registry::REGISTRY;
|
|
||||||
use crate::util::{equals, is_integer};
|
|
||||||
use serde_json::{Value, json, Map};
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, serde::Serialize)]
|
|
||||||
pub struct ValidationError {
|
|
||||||
pub code: String,
|
|
||||||
pub message: String,
|
|
||||||
pub path: String,
|
|
||||||
pub context: Value,
|
|
||||||
pub cause: Value,
|
|
||||||
pub schema_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone, Copy)]
|
|
||||||
pub struct ValidationOptions {
|
|
||||||
pub be_strict: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Validator<'a> {
|
|
||||||
options: ValidationOptions,
|
|
||||||
// The top-level root schema ID we started with
|
|
||||||
root_schema_id: String,
|
|
||||||
// Accumulated errors
|
|
||||||
errors: Vec<ValidationError>,
|
|
||||||
// Max depth to prevent stack overflow
|
|
||||||
max_depth: usize,
|
|
||||||
_phantom: std::marker::PhantomData<&'a ()>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Context passed down through the recursion
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct ValidationContext {
|
|
||||||
// Current JSON pointer path in the instance (e.g. "/users/0/name")
|
|
||||||
current_path: String,
|
|
||||||
// The properties overridden by parent schemas (for JSPG inheritance)
|
|
||||||
overrides: HashSet<String>,
|
|
||||||
// Current resolution scope for $ref (changes when following refs)
|
|
||||||
resolution_scope: String,
|
|
||||||
// Current recursion depth
|
|
||||||
depth: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ValidationContext {
|
|
||||||
fn append_path(&self, extra: &str) -> ValidationContext {
|
|
||||||
let mut new_ctx = self.clone();
|
|
||||||
if new_ctx.current_path.ends_with('/') {
|
|
||||||
new_ctx.current_path.push_str(extra);
|
|
||||||
} else if new_ctx.current_path.is_empty() {
|
|
||||||
new_ctx.current_path.push('/');
|
|
||||||
new_ctx.current_path.push_str(extra);
|
|
||||||
} else {
|
|
||||||
new_ctx.current_path.push('/');
|
|
||||||
new_ctx.current_path.push_str(extra);
|
|
||||||
}
|
|
||||||
new_ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_path_new_scope(&self, extra: &str) -> ValidationContext {
|
|
||||||
let mut new_ctx = self.append_path(extra);
|
|
||||||
// Structural recursion clears overrides
|
|
||||||
new_ctx.overrides.clear();
|
|
||||||
new_ctx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Validator<'a> {
|
|
||||||
pub fn new(options: ValidationOptions, root_schema_id: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
options,
|
|
||||||
root_schema_id: root_schema_id.to_string(),
|
|
||||||
errors: Vec::new(),
|
|
||||||
max_depth: 100,
|
|
||||||
_phantom: std::marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn validate(&mut self, schema: &Value, instance: &Value) -> Result<(), Vec<ValidationError>> {
|
|
||||||
let ctx = ValidationContext {
|
|
||||||
current_path: String::new(),
|
|
||||||
overrides: HashSet::new(),
|
|
||||||
resolution_scope: self.root_schema_id.clone(),
|
|
||||||
depth: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// We treat the top-level validate as "not lax" by default, unless specific schema logic says otherwise.
|
|
||||||
let is_lax = !self.options.be_strict;
|
|
||||||
|
|
||||||
self.validate_node(schema, instance, ctx, is_lax, false, false);
|
|
||||||
|
|
||||||
if self.errors.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(self.errors.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_node(
|
|
||||||
&mut self,
|
|
||||||
schema: &Value,
|
|
||||||
instance: &Value,
|
|
||||||
mut ctx: ValidationContext,
|
|
||||||
is_lax: bool,
|
|
||||||
skip_strict: bool,
|
|
||||||
skip_id: bool,
|
|
||||||
) -> HashSet<String> {
|
|
||||||
let mut evaluated = HashSet::new();
|
|
||||||
|
|
||||||
// Recursion limit
|
|
||||||
if ctx.depth > self.max_depth {
|
|
||||||
self.add_error("MAX_DEPTH_REACHED", "Maximum recursion depth exceeded".to_string(), instance, json!({ "depth": ctx.depth }), &ctx);
|
|
||||||
return evaluated;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.depth += 1;
|
|
||||||
|
|
||||||
// Handle Boolean Schemas
|
|
||||||
if let Value::Bool(b) = schema {
|
|
||||||
if !b {
|
|
||||||
self.add_error("FALSE_SCHEMA", "Schema is always false".to_string(), instance, Value::Null, &ctx);
|
|
||||||
}
|
|
||||||
return evaluated;
|
|
||||||
}
|
|
||||||
|
|
||||||
let schema_obj = match schema.as_object() {
|
|
||||||
Some(o) => o,
|
|
||||||
None => return evaluated, // Should be object or bool
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. Update Resolution Scope ($id)
|
|
||||||
if !skip_id {
|
|
||||||
if let Some(Value::String(id)) = schema_obj.get("$id") {
|
|
||||||
if id.contains("://") {
|
|
||||||
ctx.resolution_scope = id.clone();
|
|
||||||
} else {
|
|
||||||
if let Some(pos) = ctx.resolution_scope.rfind('/') {
|
|
||||||
let base = &ctx.resolution_scope[..pos + 1];
|
|
||||||
ctx.resolution_scope = format!("{}{}", base, id);
|
|
||||||
} else {
|
|
||||||
ctx.resolution_scope = id.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Identify Overrides (JSPG Custom Logic)
|
|
||||||
let mut inheritance_ctx = ctx.clone();
|
|
||||||
if let Some(Value::Object(props)) = schema_obj.get("properties") {
|
|
||||||
for (pname, pval) in props {
|
|
||||||
if let Some(Value::Bool(true)) = pval.get("override") {
|
|
||||||
inheritance_ctx.overrides.insert(pname.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Determine Laxness
|
|
||||||
let mut current_lax = is_lax;
|
|
||||||
if let Some(Value::Bool(true)) = schema_obj.get("unevaluatedProperties") { current_lax = true; }
|
|
||||||
if let Some(Value::Bool(true)) = schema_obj.get("additionalProperties") { current_lax = true; }
|
|
||||||
|
|
||||||
// ======== VALIDATION KEYWORDS ========
|
|
||||||
|
|
||||||
// Type
|
|
||||||
if let Some(type_val) = schema_obj.get("type") {
|
|
||||||
if !self.check_type(type_val, instance) {
|
|
||||||
let got = value_type_name(instance);
|
|
||||||
let want_json = serde_json::to_value(type_val).unwrap_or(json!("unknown"));
|
|
||||||
self.add_error("TYPE_MISMATCH", format!("Expected type {:?} but got {}", type_val, got), instance, json!({ "want": type_val, "got": got }), &ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enum
|
|
||||||
if let Some(Value::Array(vals)) = schema_obj.get("enum") {
|
|
||||||
if !vals.iter().any(|v| equals(v, instance)) {
|
|
||||||
self.add_error("ENUM_VIOLATED", "Value not in enum".to_string(), instance, json!({ "want": vals }), &ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Const
|
|
||||||
if let Some(c) = schema_obj.get("const") {
|
|
||||||
if !equals(c, instance) {
|
|
||||||
self.add_error("CONST_VIOLATED", "Value does not match constant".to_string(), instance, json!({ "want": c }), &ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object Validation
|
|
||||||
if let Value::Object(obj) = instance {
|
|
||||||
let obj_eval = self.validate_object(schema_obj, obj, instance, &ctx, current_lax);
|
|
||||||
evaluated.extend(obj_eval);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array Validation
|
|
||||||
if let Value::Array(arr) = instance {
|
|
||||||
self.validate_array(schema_obj, arr, &ctx, current_lax);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primitive Validation
|
|
||||||
self.validate_primitives(schema_obj, instance, &ctx);
|
|
||||||
|
|
||||||
// Combinators
|
|
||||||
evaluated.extend(self.validate_combinators(schema_obj, instance, &inheritance_ctx, current_lax));
|
|
||||||
|
|
||||||
// Conditionals
|
|
||||||
evaluated.extend(self.validate_conditionals(schema_obj, instance, &inheritance_ctx, current_lax));
|
|
||||||
|
|
||||||
// $ref
|
|
||||||
if let Some(Value::String(ref_str)) = schema_obj.get("$ref") {
|
|
||||||
if let Some((ref_schema, scope_uri)) = REGISTRY.resolve(ref_str, Some(&inheritance_ctx.resolution_scope)) {
|
|
||||||
let mut new_ctx = inheritance_ctx.clone();
|
|
||||||
new_ctx.resolution_scope = scope_uri;
|
|
||||||
let ref_evaluated = self.validate_node(&ref_schema, instance, new_ctx, is_lax, true, true);
|
|
||||||
evaluated.extend(ref_evaluated);
|
|
||||||
} else {
|
|
||||||
self.add_error("SCHEMA_NOT_FOUND", format!("Ref '{}' not found", ref_str), instance, json!({ "ref": ref_str }), &ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unevaluated / Strictness Check
|
|
||||||
self.check_unevaluated(schema_obj, instance, &evaluated, &ctx, current_lax, skip_strict);
|
|
||||||
|
|
||||||
evaluated
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_object(
|
|
||||||
&mut self,
|
|
||||||
schema: &Map<String, Value>,
|
|
||||||
obj: &Map<String, Value>,
|
|
||||||
instance: &Value,
|
|
||||||
ctx: &ValidationContext,
|
|
||||||
is_lax: bool,
|
|
||||||
) -> HashSet<String> {
|
|
||||||
let mut evaluated = HashSet::new();
|
|
||||||
|
|
||||||
// required
|
|
||||||
if let Some(Value::Array(req)) = schema.get("required") {
|
|
||||||
for field_val in req {
|
|
||||||
if let Some(field) = field_val.as_str() {
|
|
||||||
if !obj.contains_key(field) {
|
|
||||||
self.add_error("REQUIRED_FIELD_MISSING", format!("Required field '{}' is missing", field), &Value::Null, json!({ "want": [field] }), &ctx.append_path(field));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// properties
|
|
||||||
if let Some(Value::Object(props)) = schema.get("properties") {
|
|
||||||
for (pname, psch) in props {
|
|
||||||
if obj.contains_key(pname) {
|
|
||||||
if ctx.overrides.contains(pname) {
|
|
||||||
evaluated.insert(pname.clone());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
evaluated.insert(pname.clone());
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(pname);
|
|
||||||
self.validate_node(psch, &obj[pname], sub_ctx, is_lax, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// patternProperties
|
|
||||||
if let Some(Value::Object(pprops)) = schema.get("patternProperties") {
|
|
||||||
for (pattern, psch) in pprops {
|
|
||||||
if let Ok(re) = regex::Regex::new(pattern) {
|
|
||||||
for (pname, pval) in obj {
|
|
||||||
if re.is_match(pname) {
|
|
||||||
if ctx.overrides.contains(pname) {
|
|
||||||
evaluated.insert(pname.clone());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
evaluated.insert(pname.clone());
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(pname);
|
|
||||||
self.validate_node(psch, pval, sub_ctx, is_lax, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// additionalProperties
|
|
||||||
if let Some(apsch) = schema.get("additionalProperties") {
|
|
||||||
if apsch.is_object() || apsch.is_boolean() {
|
|
||||||
for (key, val) in obj {
|
|
||||||
let in_props = schema.get("properties").and_then(|p| p.as_object()).map_or(false, |p| p.contains_key(key));
|
|
||||||
let in_patterns = schema.get("patternProperties").and_then(|p| p.as_object()).map_or(false, |pp| {
|
|
||||||
pp.keys().any(|k| regex::Regex::new(k).map(|re| re.is_match(key)).unwrap_or(false))
|
|
||||||
});
|
|
||||||
|
|
||||||
if !in_props && !in_patterns {
|
|
||||||
evaluated.insert(key.clone());
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(key);
|
|
||||||
self.validate_node(apsch, val, sub_ctx, is_lax, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependentRequired
|
|
||||||
if let Some(Value::Object(dep_req)) = schema.get("dependentRequired") {
|
|
||||||
for (prop, required_fields_val) in dep_req {
|
|
||||||
if obj.contains_key(prop) {
|
|
||||||
if let Value::Array(required_fields) = required_fields_val {
|
|
||||||
for req_field_val in required_fields {
|
|
||||||
if let Some(req_field) = req_field_val.as_str() {
|
|
||||||
if !obj.contains_key(req_field) {
|
|
||||||
self.add_error("DEPENDENCY_FAILED", format!("Field '{}' is required when '{}' is present", req_field, prop), &Value::Null, json!({ "prop": prop, "missing": [req_field] }), &ctx.append_path(req_field));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dependentSchemas
|
|
||||||
if let Some(Value::Object(dep_sch)) = schema.get("dependentSchemas") {
|
|
||||||
for (prop, psch) in dep_sch {
|
|
||||||
if obj.contains_key(prop) {
|
|
||||||
let sub_evaluated = self.validate_node(psch, instance, ctx.clone(), is_lax, false, false);
|
|
||||||
evaluated.extend(sub_evaluated);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacy dependencies (Draft 4-7 compat)
|
|
||||||
if let Some(Value::Object(deps)) = schema.get("dependencies") {
|
|
||||||
for (prop, dep_val) in deps {
|
|
||||||
if obj.contains_key(prop) {
|
|
||||||
match dep_val {
|
|
||||||
Value::Array(arr) => {
|
|
||||||
for req_val in arr {
|
|
||||||
if let Some(req_field) = req_val.as_str() {
|
|
||||||
if !obj.contains_key(req_field) {
|
|
||||||
self.add_error(
|
|
||||||
"DEPENDENCY_FAILED",
|
|
||||||
format!("Field '{}' is required when '{}' is present", req_field, prop),
|
|
||||||
&Value::Null,
|
|
||||||
json!({ "prop": prop, "missing": [req_field] }),
|
|
||||||
&ctx.append_path(req_field),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(_) => {
|
|
||||||
// Schema dependency
|
|
||||||
let sub_evaluated = self.validate_node(dep_val, instance, ctx.clone(), is_lax, false, false);
|
|
||||||
evaluated.extend(sub_evaluated);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// minProperties / maxProperties
|
|
||||||
if let Some(min) = schema.get("minProperties").and_then(|v| v.as_u64()) {
|
|
||||||
if (obj.len() as u64) < min {
|
|
||||||
self.add_error("MIN_PROPERTIES_VIOLATED", format!("Object must have at least {} properties", min), &json!(obj.len()), json!({ "want": min, "got": obj.len() }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("maxProperties").and_then(|v| v.as_u64()) {
|
|
||||||
if (obj.len() as u64) > max {
|
|
||||||
self.add_error("MAX_PROPERTIES_VIOLATED", format!("Object must have at most {} properties", max), &json!(obj.len()), json!({ "want": max, "got": obj.len() }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
evaluated
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_array(
|
|
||||||
&mut self,
|
|
||||||
schema: &Map<String, Value>,
|
|
||||||
arr: &Vec<Value>,
|
|
||||||
ctx: &ValidationContext,
|
|
||||||
is_lax: bool,
|
|
||||||
) {
|
|
||||||
if let Some(min) = schema.get("minItems").and_then(|v| v.as_u64()) {
|
|
||||||
if (arr.len() as u64) < min {
|
|
||||||
self.add_error("MIN_ITEMS_VIOLATED", format!("Array must have at least {} items", min), &json!(arr.len()), json!({ "want": min, "got": arr.len() }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("maxItems").and_then(|v| v.as_u64()) {
|
|
||||||
if (arr.len() as u64) > max {
|
|
||||||
self.add_error("MAX_ITEMS_VIOLATED", format!("Array must have at most {} items", max), &json!(arr.len()), json!({ "want": max, "got": arr.len() }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut evaluated_index = 0;
|
|
||||||
if let Some(Value::Array(prefix)) = schema.get("prefixItems") {
|
|
||||||
for (i, psch) in prefix.iter().enumerate() {
|
|
||||||
if let Some(item) = arr.get(i) {
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(&i.to_string());
|
|
||||||
self.validate_node(psch, item, sub_ctx, is_lax, false, false);
|
|
||||||
evaluated_index = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(items_val) = schema.get("items") {
|
|
||||||
if let Value::Bool(false) = items_val {
|
|
||||||
if arr.len() > evaluated_index {
|
|
||||||
self.add_error("ADDITIONAL_ITEMS_NOT_ALLOWED", "Extra items not allowed".to_string(), &json!(arr.len()), json!({ "got": arr.len() - evaluated_index }), ctx);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Schema or true
|
|
||||||
for i in evaluated_index..arr.len() {
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(&i.to_string());
|
|
||||||
self.validate_node(items_val, &arr[i], sub_ctx, is_lax, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(contains_sch) = schema.get("contains") {
|
|
||||||
let mut matches = 0;
|
|
||||||
for (i, item) in arr.iter().enumerate() {
|
|
||||||
let mut sub = self.branch();
|
|
||||||
let sub_ctx = ctx.append_path_new_scope(&i.to_string());
|
|
||||||
sub.validate_node(contains_sch, item, sub_ctx, is_lax, false, false);
|
|
||||||
if sub.errors.is_empty() {
|
|
||||||
matches += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if matches == 0 {
|
|
||||||
self.add_error("CONTAINS_FAILED", "No items match 'contains' schema".to_string(), &json!(arr), json!({}), ctx);
|
|
||||||
}
|
|
||||||
if let Some(min) = schema.get("minContains").and_then(|v| v.as_u64()) {
|
|
||||||
if (matches as u64) < min {
|
|
||||||
self.add_error("MIN_CONTAINS_VIOLATED", format!("Expected at least {} items to match 'contains'", min), &json!(arr), json!({ "want": min, "got": matches }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("maxContains").and_then(|v| v.as_u64()) {
|
|
||||||
if (matches as u64) > max {
|
|
||||||
self.add_error("MAX_CONTAINS_VIOLATED", format!("Expected at most {} items to match 'contains'", max), &json!(arr), json!({ "want": max, "got": matches }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// uniqueItems
|
|
||||||
if let Some(Value::Bool(true)) = schema.get("uniqueItems") {
|
|
||||||
for i in 0..arr.len() {
|
|
||||||
for j in (i + 1)..arr.len() {
|
|
||||||
if equals(&arr[i], &arr[j]) {
|
|
||||||
self.add_error("UNIQUE_ITEMS_VIOLATED", format!("Array items at indices {} and {} are equal", i, j), &json!(arr), json!({ "got": [i, j] }), ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_primitives(&mut self, schema: &Map<String, Value>, instance: &Value, ctx: &ValidationContext) {
|
|
||||||
if let Some(s) = instance.as_str() {
|
|
||||||
if let Some(min) = schema.get("minLength").and_then(|v| v.as_u64()) {
|
|
||||||
if (s.chars().count() as u64) < min { self.add_error("MIN_LENGTH_VIOLATED", format!("String too short (min {})", min), instance, json!({ "want": min, "got": s.len() }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("maxLength").and_then(|v| v.as_u64()) {
|
|
||||||
if (s.chars().count() as u64) > max { self.add_error("MAX_LENGTH_VIOLATED", format!("String too long (max {})", max), instance, json!({ "want": max, "got": s.len() }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(Value::String(pat)) = schema.get("pattern") {
|
|
||||||
if let Ok(re) = regex::Regex::new(pat) {
|
|
||||||
if !re.is_match(s) { self.add_error("PATTERN_VIOLATED", format!("String does not match pattern '{}'", pat), instance, json!({ "want": pat, "got": s }), ctx); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(Value::String(fmt)) = schema.get("format") {
|
|
||||||
if !s.is_empty() {
|
|
||||||
match fmt.as_str() {
|
|
||||||
"uuid" => { if uuid::Uuid::parse_str(s).is_err() { self.add_error("FORMAT_INVALID", format!("Value '{}' is not a valid UUID", s), instance, json!({ "format": "uuid" }), ctx); } }
|
|
||||||
"date-time" => { if chrono::DateTime::parse_from_rfc3339(s).is_err() { self.add_error("FORMAT_INVALID", format!("Value '{}' is not a valid date-time", s), instance, json!({ "format": "date-time" }), ctx); } }
|
|
||||||
"email" => { if !s.contains('@') { self.add_error("FORMAT_INVALID", format!("Value '{}' is not a valid email", s), instance, json!({ "format": "email" }), ctx); } }
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(n) = instance.as_f64() {
|
|
||||||
if let Some(min) = schema.get("minimum").and_then(|v| v.as_f64()) {
|
|
||||||
if n < min { self.add_error("MINIMUM_VIOLATED", format!("Value {} < minimum {}", n, min), instance, json!({ "want": min, "got": n }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("maximum").and_then(|v| v.as_f64()) {
|
|
||||||
if n > max { self.add_error("MAXIMUM_VIOLATED", format!("Value {} > maximum {}", n, max), instance, json!({ "want": max, "got": n }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(min) = schema.get("exclusiveMinimum").and_then(|v| v.as_f64()) {
|
|
||||||
if n <= min { self.add_error("EXCLUSIVE_MINIMUM_VIOLATED", format!("Value {} <= exclusive minimum {}", n, min), instance, json!({ "want": min, "got": n }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(max) = schema.get("exclusiveMaximum").and_then(|v| v.as_f64()) {
|
|
||||||
if n >= max { self.add_error("EXCLUSIVE_MAXIMUM_VIOLATED", format!("Value {} >= exclusive maximum {}", n, max), instance, json!({ "want": max, "got": n }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(mult) = schema.get("multipleOf").and_then(|v| v.as_f64()) {
|
|
||||||
let rem = (n / mult).fract();
|
|
||||||
if rem.abs() > f64::EPSILON && (1.0 - rem).abs() > f64::EPSILON {
|
|
||||||
self.add_error("MULTIPLE_OF_VIOLATED", format!("Value {} not multiple of {}", n, mult), instance, json!({ "want": mult, "got": n }), ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_combinators(&mut self, schema: &Map<String, Value>, instance: &Value, ctx: &ValidationContext, is_lax: bool) -> HashSet<String> {
|
|
||||||
let mut evaluated = HashSet::new();
|
|
||||||
if let Some(Value::Array(all_of)) = schema.get("allOf") {
|
|
||||||
for sch in all_of { evaluated.extend(self.validate_node(sch, instance, ctx.clone(), is_lax, true, false)); }
|
|
||||||
}
|
|
||||||
if let Some(Value::Array(any_of)) = schema.get("anyOf") {
|
|
||||||
let mut matched = false;
|
|
||||||
let mut errors_acc = Vec::new();
|
|
||||||
for sch in any_of {
|
|
||||||
let mut sub = self.branch();
|
|
||||||
let sub_eval = sub.validate_node(sch, instance, ctx.clone(), is_lax, false, false);
|
|
||||||
if sub.errors.is_empty() { matched = true; evaluated.extend(sub_eval); } else { errors_acc.extend(sub.errors); }
|
|
||||||
}
|
|
||||||
if !matched { self.add_error("ANY_OF_VIOLATED", "Value did not match any allowed schema".to_string(), instance, json!({ "causes": errors_acc }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(Value::Array(one_of)) = schema.get("oneOf") {
|
|
||||||
let mut match_count = 0;
|
|
||||||
let mut last_eval = HashSet::new();
|
|
||||||
let mut error_causes = Vec::new();
|
|
||||||
for sch in one_of {
|
|
||||||
let mut sub = self.branch();
|
|
||||||
let sub_eval = sub.validate_node(sch, instance, ctx.clone(), is_lax, false, false);
|
|
||||||
if sub.errors.is_empty() { match_count += 1; last_eval = sub_eval; } else { error_causes.extend(sub.errors); }
|
|
||||||
}
|
|
||||||
if match_count == 1 { evaluated.extend(last_eval); }
|
|
||||||
else { self.add_error("ONE_OF_VIOLATED", format!("Value matched {} schemas, expected 1", match_count), instance, json!({ "matched": match_count, "causes": error_causes }), ctx); }
|
|
||||||
}
|
|
||||||
if let Some(not_sch) = schema.get("not") {
|
|
||||||
let mut sub = self.branch();
|
|
||||||
sub.validate_node(not_sch, instance, ctx.clone(), is_lax, false, false);
|
|
||||||
if sub.errors.is_empty() { self.add_error("NOT_VIOLATED", "Value matched 'not' schema".to_string(), instance, Value::Null, ctx); }
|
|
||||||
}
|
|
||||||
evaluated
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validate_conditionals(&mut self, schema: &Map<String, Value>, instance: &Value, ctx: &ValidationContext, is_lax: bool) -> HashSet<String> {
|
|
||||||
let mut evaluated = HashSet::new();
|
|
||||||
if let Some(if_sch) = schema.get("if") {
|
|
||||||
let mut sub = self.branch();
|
|
||||||
let sub_eval = sub.validate_node(if_sch, instance, ctx.clone(), is_lax, true, false);
|
|
||||||
if sub.errors.is_empty() {
|
|
||||||
evaluated.extend(sub_eval);
|
|
||||||
if let Some(then_sch) = schema.get("then") { evaluated.extend(self.validate_node(then_sch, instance, ctx.clone(), is_lax, false, false)); }
|
|
||||||
} else if let Some(else_sch) = schema.get("else") {
|
|
||||||
evaluated.extend(self.validate_node(else_sch, instance, ctx.clone(), is_lax, false, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
evaluated
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_unevaluated(&mut self, schema: &Map<String, Value>, instance: &Value, evaluated: &HashSet<String>, ctx: &ValidationContext, is_lax: bool, skip_strict: bool) {
|
|
||||||
if let Value::Object(obj) = instance {
|
|
||||||
if let Some(Value::Bool(false)) = schema.get("additionalProperties") {
|
|
||||||
for key in obj.keys() {
|
|
||||||
let in_props = schema.get("properties").and_then(|p| p.as_object()).map_or(false, |p| p.contains_key(key));
|
|
||||||
let in_pattern = schema.get("patternProperties").and_then(|p| p.as_object()).map_or(false, |pp| pp.keys().any(|k| regex::Regex::new(k).map(|re| re.is_match(key)).unwrap_or(false)));
|
|
||||||
if !in_props && !in_pattern {
|
|
||||||
if ctx.overrides.contains(key) { continue; }
|
|
||||||
self.add_error("ADDITIONAL_PROPERTIES_NOT_ALLOWED", format!("Property '{}' is not allowed", key), &Value::Null, json!({ "got": [key] }), &ctx.append_path(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let explicit_opts = schema.contains_key("unevaluatedProperties") || schema.contains_key("additionalProperties");
|
|
||||||
let should_check_strict = self.options.be_strict && !is_lax && !explicit_opts && !skip_strict;
|
|
||||||
let check_unevaluated = matches!(schema.get("unevaluatedProperties"), Some(Value::Bool(false)));
|
|
||||||
if should_check_strict || check_unevaluated {
|
|
||||||
for key in obj.keys() {
|
|
||||||
if !evaluated.contains(key) {
|
|
||||||
if ctx.overrides.contains(key) { continue; }
|
|
||||||
self.add_error("ADDITIONAL_PROPERTIES_NOT_ALLOWED", format!("Property '{}' is not allowed (strict/unevaluated)", key), &Value::Null, json!({ "got": [key] }), &ctx.append_path(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_type(&self, expected: &Value, instance: &Value) -> bool {
|
|
||||||
match expected {
|
|
||||||
Value::String(s) => self.is_primitive_type(s, instance),
|
|
||||||
Value::Array(arr) => arr.iter().filter_map(|v| v.as_str()).any(|pt| self.is_primitive_type(pt, instance)),
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_primitive_type(&self, pt: &str, instance: &Value) -> bool {
|
|
||||||
match pt {
|
|
||||||
"string" => instance.is_string(),
|
|
||||||
"number" => instance.is_number(),
|
|
||||||
"integer" => is_integer(instance),
|
|
||||||
"boolean" => instance.is_boolean(),
|
|
||||||
"array" => instance.is_array(),
|
|
||||||
"object" => instance.is_object(),
|
|
||||||
"null" => instance.is_null(),
|
|
||||||
_ => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn branch(&self) -> Self {
|
|
||||||
Self { options: self.options, root_schema_id: self.root_schema_id.clone(), errors: Vec::new(), max_depth: self.max_depth, _phantom: std::marker::PhantomData }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_error(&mut self, code: &str, message: String, context: &Value, cause: Value, ctx: &ValidationContext) {
|
|
||||||
let path = ctx.current_path.clone();
|
|
||||||
if self.errors.iter().any(|e| e.code == code && e.path == path) { return; }
|
|
||||||
self.errors.push(ValidationError { code: code.to_string(), message, path, context: context.clone(), cause, schema_id: self.root_schema_id.clone() });
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extend_unique(&mut self, errors: Vec<ValidationError>) {
|
|
||||||
for e in errors { if !self.errors.iter().any(|existing| existing.code == e.code && existing.path == e.path) { self.errors.push(e); } }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn value_type_name(v: &Value) -> &'static str {
|
|
||||||
match v {
|
|
||||||
Value::Null => "null",
|
|
||||||
Value::Bool(_) => "boolean",
|
|
||||||
Value::Number(n) => if n.is_i64() { "integer" } else { "number" },
|
|
||||||
Value::String(_) => "string",
|
|
||||||
Value::Array(_) => "array",
|
|
||||||
Value::Object(_) => "object",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
use serde_json::Value;
|
|
||||||
use pgrx::JsonB;
|
|
||||||
|
|
||||||
// Simple test helpers for cleaner test code
|
|
||||||
pub fn assert_success(result: &JsonB) {
|
|
||||||
let json = &result.0;
|
|
||||||
if !json.get("response").is_some() || json.get("errors").is_some() {
|
|
||||||
let pretty = serde_json::to_string_pretty(json).unwrap_or_else(|_| format!("{:?}", json));
|
|
||||||
panic!("Expected success but got:\n{}", pretty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_failure(result: &JsonB) {
|
|
||||||
let json = &result.0;
|
|
||||||
if json.get("response").is_some() || !json.get("errors").is_some() {
|
|
||||||
let pretty = serde_json::to_string_pretty(json).unwrap_or_else(|_| format!("{:?}", json));
|
|
||||||
panic!("Expected failure but got:\n{}", pretty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_error_count(result: &JsonB, expected_count: usize) {
|
|
||||||
assert_failure(result);
|
|
||||||
let errors = get_errors(result);
|
|
||||||
if errors.len() != expected_count {
|
|
||||||
let pretty = serde_json::to_string_pretty(&result.0).unwrap_or_else(|_| format!("{:?}", result.0));
|
|
||||||
panic!("Expected {} errors, got {}:\n{}", expected_count, errors.len(), pretty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_errors(result: &JsonB) -> &Vec<Value> {
|
|
||||||
result.0["errors"].as_array().expect("errors should be an array")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has_error_with_code(result: &JsonB, code: &str) -> bool {
|
|
||||||
get_errors(result).iter().any(|e| e["code"] == code)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn has_error_with_code_and_path(result: &JsonB, code: &str, path: &str) -> bool {
|
|
||||||
get_errors(result).iter().any(|e| e["code"] == code && e["details"]["path"] == path)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_has_error(result: &JsonB, code: &str, path: &str) {
|
|
||||||
if !has_error_with_code_and_path(result, code, path) {
|
|
||||||
let pretty = serde_json::to_string_pretty(&result.0).unwrap_or_else(|_| format!("{:?}", result.0));
|
|
||||||
panic!("Expected error with code='{}' and path='{}' but not found:\n{}", code, path, pretty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn find_error_with_code<'a>(result: &'a JsonB, code: &str) -> &'a Value {
|
|
||||||
get_errors(result).iter().find(|e| e["code"] == code)
|
|
||||||
.unwrap_or_else(|| panic!("No error found with code '{}'", code))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn find_error_with_code_and_path<'a>(result: &'a JsonB, code: &str, path: &str) -> &'a Value {
|
|
||||||
get_errors(result).iter().find(|e| e["code"] == code && e["details"]["path"] == path)
|
|
||||||
.unwrap_or_else(|| panic!("No error found with code '{}' and path '{}'", code, path))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_error_detail(error: &Value, detail_key: &str, expected_value: &str) {
|
|
||||||
let actual = error["details"][detail_key].as_str()
|
|
||||||
.unwrap_or_else(|| panic!("Error detail '{}' is not a string", detail_key));
|
|
||||||
assert_eq!(actual, expected_value, "Error detail '{}' mismatch", detail_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Additional convenience helpers for common patterns
|
|
||||||
|
|
||||||
pub fn assert_error_message_contains(error: &Value, substring: &str) {
|
|
||||||
let message = error["message"].as_str().expect("error should have message");
|
|
||||||
assert!(message.contains(substring), "Expected message to contain '{}', got '{}'", substring, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_error_cause_json(error: &Value, expected_cause: &Value) {
|
|
||||||
let cause = &error["details"]["cause"];
|
|
||||||
assert!(cause.is_object(), "cause should be JSON object");
|
|
||||||
assert_eq!(cause, expected_cause, "cause mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_error_context(error: &Value, expected_context: &Value) {
|
|
||||||
assert_eq!(&error["details"]["context"], expected_context, "context mismatch");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn jsonb(val: Value) -> JsonB {
|
|
||||||
JsonB(val)
|
|
||||||
}
|
|
||||||
1128
old_tests/schemas.rs
1128
old_tests/schemas.rs
File diff suppressed because it is too large
Load Diff
1089
old_tests/tests.rs
1089
old_tests/tests.rs
File diff suppressed because it is too large
Load Diff
106
puncs_6_fix.txt
106
puncs_6_fix.txt
@ -1,106 +0,0 @@
|
|||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.39s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"name", "job_id", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "name", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"name", "manager_id", "job_id", "type"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"name", "manager_id", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"name", "manager_id", "job_id", "type", "nested_or_super_job"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"name", "manager_id", "job_id", "type", "nested_or_super_job", "root_job"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"job_id", "name", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "name", "type", "manager_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"type", "job_id", "manager_id", "name"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"job_id", "name", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"type", "name", "job_id"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"job_id", "name", "manager_id", "type", "nested_or_super_job"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"root_job", "job_id", "name", "manager_id", "type", "nested_or_super_job"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/my_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/my_job/type. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: validate_object inserted 'my_job' at /nested_or_super_job/my_job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"job_id", "name", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
|
|
||||||
thread 'test_puncs_6' (15118678) panicked at tests/tests.rs:150:44:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[complex punc type matching with oneOf and nested refs] Test 'valid person against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'first_name'\", details: ErrorDetails { path: \"/first_name\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against strict punc' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_puncs_6 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_puncs_6
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.01s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
103
puncs_6_full.txt
103
puncs_6_full.txt
@ -1,103 +0,0 @@
|
|||||||
Blocking waiting for file lock on artifact directory
|
|
||||||
Compiling jspg v0.1.0 (/Users/awgneo/Repositories/thoughtpatterns/cellular/jspg)
|
|
||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 7.63s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"name", "job_id", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"manager_id", "type", "name", "job_id"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"name", "job_id", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"name", "job_id", "nested_or_super_job", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"root_job", "name", "job_id", "nested_or_super_job", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"job_id", "manager_id", "name"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"type", "job_id", "manager_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"name", "manager_id", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"type", "job_id", "name"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"name", "manager_id", "job_id", "type", "nested_or_super_job"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"name", "root_job", "manager_id", "job_id", "type", "nested_or_super_job"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/my_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/my_job/type. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'my_job' at /nested_or_super_job/my_job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"name", "job_id", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "manager_id", "name", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
|
|
||||||
thread 'test_puncs_6' (15113120) panicked at tests/tests.rs:150:44:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[complex punc type matching with oneOf and nested refs] Test 'valid person against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'first_name'\", details: ErrorDetails { path: \"/first_name\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against strict punc' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_puncs_6 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_puncs_6
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.01s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.47s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "type", "manager_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"type", "name", "manager_id", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"manager_id", "type", "job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"manager_id", "job_id", "type", "name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/my_job/name
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"type", "name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"manager_id", "type", "name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
|
|
||||||
thread 'test_puncs_6' (15109801) panicked at tests/tests.rs:150:44:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[complex punc type matching with oneOf and nested refs] Test 'valid person against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'first_name'\", details: ErrorDetails { path: \"/first_name\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against strict punc' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_puncs_6 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_puncs_6
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.00s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
@ -1,106 +0,0 @@
|
|||||||
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.41s
|
|
||||||
Running tests/tests.rs (target/debug/deps/tests-0f6b1e496850f0af)
|
|
||||||
|
|
||||||
running 1 test
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"name", "job_id", "manager_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"name", "job_id", "manager_id", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"name", "manager_id", "job_id", "type"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"job_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"job_id", "nested_or_super_job", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"job_id", "nested_or_super_job", "manager_id", "type", "name", "root_job"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"manager_id", "name", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"manager_id", "name", "job_id", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: validate_object inserted 'nested_or_super_job' at /nested_or_super_job. Keys: {"type", "manager_id", "job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /root_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /root_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /root_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /root_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /root_job/type. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "type", "job_id"}
|
|
||||||
DEBUG: check_strictness at /root_job. Extensible: false. Keys: {"type", "name", "job_id"}
|
|
||||||
DEBUG: validate_object inserted 'root_job' at /root_job. Keys: {"type", "manager_id", "job_id", "name", "nested_or_super_job"}
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {"type", "root_job", "manager_id", "job_id", "name", "nested_or_super_job"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/my_job/name. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/my_job/type. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"type", "name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/my_job. Extensible: false. Keys: {"name", "type"}
|
|
||||||
DEBUG: validate_object inserted 'my_job' at /nested_or_super_job/my_job. Keys: {"name", "type"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/name. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'name' at /nested_or_super_job/name. Keys: {}
|
|
||||||
DEBUG: validate_refs merging res from entity. Keys: {"name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/job_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'job_id' at /nested_or_super_job/job_id. Keys: {"name"}
|
|
||||||
DEBUG: validate_refs merging res from job. Keys: {"name", "job_id"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/manager_id. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'manager_id' at /nested_or_super_job/manager_id. Keys: {"job_id", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job/type. Extensible: false. Keys: {}
|
|
||||||
DEBUG: validate_object inserted 'type' at /nested_or_super_job/type. Keys: {"manager_id", "job_id", "name"}
|
|
||||||
DEBUG: validate_refs merging res from super_job. Keys: {"job_id", "manager_id", "type", "name"}
|
|
||||||
DEBUG: check_strictness at /nested_or_super_job. Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("polymorphic_org_punc.request") ref=Some("organization.family")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
DEBUG: VALIDATE ROOT: id=Some("strict_org_punc.request") ref=Some("organization")
|
|
||||||
DEBUG: check_strictness at . Extensible: false. Keys: {}
|
|
||||||
|
|
||||||
thread 'test_puncs_6' (15121282) panicked at tests/tests.rs:150:44:
|
|
||||||
called `Result::unwrap()` on an `Err` value: "[complex punc type matching with oneOf and nested refs] Test 'valid person against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'first_name'\", details: ErrorDetails { path: \"/first_name\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against organization punc (polymorphic)' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]\n[complex punc type matching with oneOf and nested refs] Test 'valid organization against strict punc' failed. Expected: true, Got: true. Errors: [Error { punc: None, code: \"STRICT_PROPERTY_VIOLATION\", message: \"Unexpected property 'id'\", details: ErrorDetails { path: \"/id\" } }]"
|
|
||||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
|
||||||
test test_puncs_6 ... FAILED
|
|
||||||
|
|
||||||
failures:
|
|
||||||
|
|
||||||
failures:
|
|
||||||
test_puncs_6
|
|
||||||
|
|
||||||
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 338 filtered out; finished in 0.01s
|
|
||||||
|
|
||||||
error: test failed, to rerun pass `--test tests`
|
|
||||||
@ -379,7 +379,7 @@ impl Compiler {
|
|||||||
// Schema struct modifications require &mut.
|
// Schema struct modifications require &mut.
|
||||||
|
|
||||||
let mut final_schema = Arc::try_unwrap(root).unwrap_or_else(|arc| (*arc).clone());
|
let mut final_schema = Arc::try_unwrap(root).unwrap_or_else(|arc| (*arc).clone());
|
||||||
final_schema.obj.compiled_schemas = Some(Arc::new(registry));
|
final_schema.obj.compiled_registry = Some(Arc::new(registry));
|
||||||
|
|
||||||
Arc::new(final_schema)
|
Arc::new(final_schema)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ pub struct Drop {
|
|||||||
// as they are added by the SQL wrapper. We just need to conform to the structure.
|
// as they are added by the SQL wrapper. We just need to conform to the structure.
|
||||||
// The user said "Validator::validate always needs to return this drop type".
|
// The user said "Validator::validate always needs to return this drop type".
|
||||||
// So we should match it as closely as possible.
|
// So we should match it as closely as possible.
|
||||||
|
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
pub type_: String, // "drop"
|
pub type_: String, // "drop"
|
||||||
|
|
||||||
@ -35,6 +34,14 @@ impl Drop {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn success_with_val(val: Value) -> Self {
|
||||||
|
Self {
|
||||||
|
type_: "drop".to_string(),
|
||||||
|
response: Some(val),
|
||||||
|
errors: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn with_errors(errors: Vec<Error>) -> Self {
|
pub fn with_errors(errors: Vec<Error>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
type_: "drop".to_string(),
|
type_: "drop".to_string(),
|
||||||
|
|||||||
173
src/lib.rs
173
src/lib.rs
@ -11,14 +11,23 @@ mod schema;
|
|||||||
pub mod util;
|
pub mod util;
|
||||||
mod validator;
|
mod validator;
|
||||||
|
|
||||||
use crate::registry::REGISTRY;
|
|
||||||
use crate::schema::Schema;
|
use crate::schema::Schema;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
// Global Atomic Swap Container:
|
||||||
|
// - RwLock: To protect the SWAP of the Option.
|
||||||
|
// - Option: Because it starts empty.
|
||||||
|
// - Arc: Because multiple running threads might hold the OLD validator while we swap.
|
||||||
|
// - Validator: It immutably owns the Registry.
|
||||||
|
static ref GLOBAL_VALIDATOR: RwLock<Option<Arc<validator::Validator>>> = RwLock::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
#[pg_extern(strict)]
|
#[pg_extern(strict)]
|
||||||
fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
||||||
let mut registry = REGISTRY.write().unwrap();
|
// 1. Build a new Registry LOCALLY (on stack)
|
||||||
registry.clear();
|
let mut registry = registry::Registry::new();
|
||||||
|
|
||||||
// Generate Family Schemas from Types
|
// Generate Family Schemas from Types
|
||||||
{
|
{
|
||||||
@ -54,8 +63,7 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(schema) = serde_json::from_value::<Schema>(schema_json) {
|
if let Ok(schema) = serde_json::from_value::<Schema>(schema_json) {
|
||||||
let compiled = crate::compiler::Compiler::compile(schema, Some(id.clone()));
|
registry.add(schema);
|
||||||
registry.insert(id, compiled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,14 +82,8 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
|||||||
for schema_val in schemas {
|
for schema_val in schemas {
|
||||||
// Deserialize into our robust Schema struct to ensure validity/parsing
|
// Deserialize into our robust Schema struct to ensure validity/parsing
|
||||||
if let Ok(schema) = serde_json::from_value::<Schema>(schema_val.clone()) {
|
if let Ok(schema) = serde_json::from_value::<Schema>(schema_val.clone()) {
|
||||||
if let Some(id) = &schema.obj.id {
|
// Registry handles compilation
|
||||||
let id_clone = id.clone();
|
registry.add(schema);
|
||||||
// Store the compiled Schema in the registry.
|
|
||||||
// The registry.insert method now handles simple insertion of CompiledSchema
|
|
||||||
let compiled =
|
|
||||||
crate::compiler::Compiler::compile(schema, Some(id_clone.clone()));
|
|
||||||
registry.insert(id_clone, compiled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,35 +96,158 @@ fn cache_json_schemas(enums: JsonB, types: JsonB, puncs: JsonB) -> JsonB {
|
|||||||
cache_items(types);
|
cache_items(types);
|
||||||
cache_items(puncs); // public/private distinction logic to come later
|
cache_items(puncs); // public/private distinction logic to come later
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Wrap in Validator and Arc
|
||||||
|
let new_validator = validator::Validator::new(registry);
|
||||||
|
let new_arc = Arc::new(new_validator);
|
||||||
|
|
||||||
|
// 3. ATOMIC SWAP
|
||||||
|
{
|
||||||
|
let mut lock = GLOBAL_VALIDATOR.write().unwrap();
|
||||||
|
*lock = Some(new_arc);
|
||||||
|
}
|
||||||
|
|
||||||
JsonB(json!({ "response": "success" }))
|
JsonB(json!({ "response": "success" }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
#[pg_extern(strict, parallel_safe)]
|
||||||
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
fn mask_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
||||||
let drop = validator::Validator::validate(schema_id, &instance.0);
|
// 1. Acquire Snapshot
|
||||||
|
let validator_arc = {
|
||||||
|
let lock = GLOBAL_VALIDATOR.read().unwrap();
|
||||||
|
lock.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Validate (Lock-Free)
|
||||||
|
if let Some(validator) = validator_arc {
|
||||||
|
// We need a mutable copy of the value to mask it
|
||||||
|
let mut mutable_instance = instance.0.clone();
|
||||||
|
|
||||||
|
match validator.mask(schema_id, &mut mutable_instance) {
|
||||||
|
Ok(result) => {
|
||||||
|
// If valid, return the MASKED instance
|
||||||
|
if result.is_valid() {
|
||||||
|
let drop = crate::drop::Drop::success_with_val(mutable_instance);
|
||||||
JsonB(serde_json::to_value(drop).unwrap())
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
} else {
|
||||||
|
// If invalid, return errors (Schema Validation Errors)
|
||||||
|
let errors: Vec<crate::drop::Error> = result
|
||||||
|
.errors
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| crate::drop::Error {
|
||||||
|
punc: None,
|
||||||
|
code: e.code,
|
||||||
|
message: e.message,
|
||||||
|
details: crate::drop::ErrorDetails { path: e.path },
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let drop = crate::drop::Drop::with_errors(errors);
|
||||||
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Schema Not Found or other fatal error
|
||||||
|
let error = crate::drop::Error {
|
||||||
|
punc: None,
|
||||||
|
code: e.code,
|
||||||
|
message: e.message,
|
||||||
|
details: crate::drop::ErrorDetails { path: e.path },
|
||||||
|
};
|
||||||
|
let drop = crate::drop::Drop::with_errors(vec![error]);
|
||||||
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JsonB(json!({
|
||||||
|
"punc": null,
|
||||||
|
"errors": [{
|
||||||
|
"code": "VALIDATOR_NOT_INITIALIZED",
|
||||||
|
"message": "JSON Schemas have not been cached yet. Run cache_json_schemas()",
|
||||||
|
"details": { "path": "" }
|
||||||
|
}]
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_extern(strict, parallel_safe)]
|
||||||
|
fn validate_json_schema(schema_id: &str, instance: JsonB) -> JsonB {
|
||||||
|
// 1. Acquire Snapshot
|
||||||
|
let validator_arc = {
|
||||||
|
let lock = GLOBAL_VALIDATOR.read().unwrap();
|
||||||
|
lock.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
// 2. Validate (Lock-Free)
|
||||||
|
if let Some(validator) = validator_arc {
|
||||||
|
match validator.validate(schema_id, &instance.0) {
|
||||||
|
Ok(result) => {
|
||||||
|
if result.is_valid() {
|
||||||
|
let drop = crate::drop::Drop::success();
|
||||||
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
} else {
|
||||||
|
let errors: Vec<crate::drop::Error> = result
|
||||||
|
.errors
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| crate::drop::Error {
|
||||||
|
punc: None,
|
||||||
|
code: e.code,
|
||||||
|
message: e.message,
|
||||||
|
details: crate::drop::ErrorDetails { path: e.path },
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let drop = crate::drop::Drop::with_errors(errors);
|
||||||
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let error = crate::drop::Error {
|
||||||
|
punc: None,
|
||||||
|
code: e.code,
|
||||||
|
message: e.message,
|
||||||
|
details: crate::drop::ErrorDetails { path: e.path },
|
||||||
|
};
|
||||||
|
let drop = crate::drop::Drop::with_errors(vec![error]);
|
||||||
|
JsonB(serde_json::to_value(drop).unwrap())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JsonB(json!({
|
||||||
|
"punc": null,
|
||||||
|
"errors": [{
|
||||||
|
"code": "VALIDATOR_NOT_INITIALIZED",
|
||||||
|
"message": "JSON Schemas have not been cached yet. Run cache_json_schemas()",
|
||||||
|
"details": { "path": "" }
|
||||||
|
}]
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
#[pg_extern(strict, parallel_safe)]
|
||||||
fn json_schema_cached(schema_id: &str) -> bool {
|
fn json_schema_cached(schema_id: &str) -> bool {
|
||||||
let registry = REGISTRY.read().unwrap();
|
if let Some(validator) = GLOBAL_VALIDATOR.read().unwrap().as_ref() {
|
||||||
registry.get(schema_id).is_some()
|
match validator.validate(schema_id, &serde_json::Value::Null) {
|
||||||
|
Err(e) if e.code == "SCHEMA_NOT_FOUND" => false,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern(strict)]
|
#[pg_extern(strict)]
|
||||||
fn clear_json_schemas() -> JsonB {
|
fn clear_json_schemas() -> JsonB {
|
||||||
let mut registry = REGISTRY.write().unwrap();
|
let mut lock = GLOBAL_VALIDATOR.write().unwrap();
|
||||||
registry.clear();
|
*lock = None;
|
||||||
JsonB(json!({ "response": "success" }))
|
JsonB(json!({ "response": "success" }))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pg_extern(strict, parallel_safe)]
|
#[pg_extern(strict, parallel_safe)]
|
||||||
fn show_json_schemas() -> JsonB {
|
fn show_json_schemas() -> JsonB {
|
||||||
let registry = REGISTRY.read().unwrap();
|
if let Some(_validator) = GLOBAL_VALIDATOR.read().unwrap().as_ref() {
|
||||||
// Debug dump
|
JsonB(json!({ "response": "success", "status": "active" }))
|
||||||
// In a real scenario we might return the whole map, but for now just success
|
} else {
|
||||||
// or maybe a list of keys
|
JsonB(json!({ "response": "success", "status": "empty" }))
|
||||||
JsonB(json!({ "response": "success", "count": registry.len() }))
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "pg_test"))]
|
#[cfg(any(test, feature = "pg_test"))]
|
||||||
|
|||||||
@ -21,6 +21,16 @@ impl Registry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add(&mut self, schema: crate::schema::Schema) {
|
||||||
|
let id = schema
|
||||||
|
.obj
|
||||||
|
.id
|
||||||
|
.clone()
|
||||||
|
.expect("Schema must have an $id to be registered");
|
||||||
|
let compiled = crate::compiler::Compiler::compile(schema, Some(id.clone()));
|
||||||
|
self.schemas.insert(id, compiled);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, id: String, schema: Arc<Schema>) {
|
pub fn insert(&mut self, id: String, schema: Arc<Schema>) {
|
||||||
// We allow overwriting for now to support re-compilation in tests/dev
|
// We allow overwriting for now to support re-compilation in tests/dev
|
||||||
self.schemas.insert(id, schema);
|
self.schemas.insert(id, schema);
|
||||||
|
|||||||
@ -139,7 +139,7 @@ pub struct SchemaObject {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub compiled_pattern_properties: Option<Vec<(crate::compiler::CompiledRegex, Arc<Schema>)>>,
|
pub compiled_pattern_properties: Option<Vec<(crate::compiler::CompiledRegex, Arc<Schema>)>>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub compiled_schemas: Option<Arc<crate::registry::Registry>>,
|
pub compiled_registry: Option<Arc<crate::registry::Registry>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
|
|||||||
24
src/tests.rs
24
src/tests.rs
@ -1097,6 +1097,30 @@ fn test_pattern_1() {
|
|||||||
crate::util::run_test_file_at_index(&path, 1).unwrap();
|
crate::util::run_test_file_at_index(&path, 1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[pg_test]
|
||||||
|
fn test_masking_0() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::util::run_test_file_at_index(&path, 0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_test]
|
||||||
|
fn test_masking_1() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::util::run_test_file_at_index(&path, 1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_test]
|
||||||
|
fn test_masking_2() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::util::run_test_file_at_index(&path, 2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[pg_test]
|
||||||
|
fn test_masking_3() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
crate::util::run_test_file_at_index(&path, 3).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[pg_test]
|
#[pg_test]
|
||||||
fn test_max_properties_0() {
|
fn test_max_properties_0() {
|
||||||
let path = format!("{}/tests/fixtures/maxProperties.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/tests/fixtures/maxProperties.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
|||||||
168
src/util.rs
168
src/util.rs
@ -25,7 +25,7 @@ struct TestCase {
|
|||||||
expected: Option<serde_json::Value>,
|
expected: Option<serde_json::Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::registry::REGISTRY;
|
// use crate::registry::REGISTRY; // No longer used directly for tests!
|
||||||
use crate::validator::Validator;
|
use crate::validator::Validator;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
@ -38,12 +38,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
||||||
// Clear registry to ensure isolation
|
|
||||||
// {
|
|
||||||
// let mut registry = REGISTRY.write().unwrap();
|
|
||||||
// registry.clear();
|
|
||||||
// }
|
|
||||||
|
|
||||||
let content =
|
let content =
|
||||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", path));
|
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", path));
|
||||||
let suite: Vec<TestSuite> = serde_json::from_str(&content)
|
let suite: Vec<TestSuite> = serde_json::from_str(&content)
|
||||||
@ -56,6 +50,7 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
let group = &suite[index];
|
let group = &suite[index];
|
||||||
let mut failures = Vec::<String>::new();
|
let mut failures = Vec::<String>::new();
|
||||||
|
|
||||||
|
// Create Local Registry for this test group
|
||||||
let mut registry = crate::registry::Registry::new();
|
let mut registry = crate::registry::Registry::new();
|
||||||
|
|
||||||
// Helper to register items with 'schemas'
|
// Helper to register items with 'schemas'
|
||||||
@ -69,12 +64,7 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
if let Ok(schema) =
|
if let Ok(schema) =
|
||||||
serde_json::from_value::<crate::schema::Schema>(schema_val.clone())
|
serde_json::from_value::<crate::schema::Schema>(schema_val.clone())
|
||||||
{
|
{
|
||||||
// Clone ID upfront to avoid borrow issues
|
registry.add(schema);
|
||||||
if let Some(id_clone) = schema.obj.id.clone() {
|
|
||||||
let compiled =
|
|
||||||
crate::compiler::Compiler::compile(schema, Some(id_clone.clone()));
|
|
||||||
registry.insert(id_clone, compiled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,8 +108,7 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(schema) = serde_json::from_value::<crate::schema::Schema>(schema_json) {
|
if let Ok(schema) = serde_json::from_value::<crate::schema::Schema>(schema_json) {
|
||||||
let compiled = crate::compiler::Compiler::compile(schema, Some(id.clone()));
|
registry.add(schema);
|
||||||
registry.insert(id, compiled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,20 +123,16 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
// Some tests use a raw 'schema' or 'schemas' field at the group level
|
// Some tests use a raw 'schema' or 'schemas' field at the group level
|
||||||
if let Some(schema_val) = &group.schema {
|
if let Some(schema_val) = &group.schema {
|
||||||
match serde_json::from_value::<crate::schema::Schema>(schema_val.clone()) {
|
match serde_json::from_value::<crate::schema::Schema>(schema_val.clone()) {
|
||||||
Ok(schema) => {
|
Ok(mut schema) => {
|
||||||
let id = schema
|
let id_clone = schema.obj.id.clone();
|
||||||
.obj
|
if id_clone.is_some() {
|
||||||
.id
|
registry.add(schema);
|
||||||
.clone()
|
} else {
|
||||||
.or_else(|| {
|
|
||||||
// Fallback ID if none provided in schema
|
// Fallback ID if none provided in schema
|
||||||
Some(format!("test:{}:{}", path, index))
|
let id = format!("test:{}:{}", path, index);
|
||||||
})
|
schema.obj.id = Some(id);
|
||||||
.unwrap();
|
registry.add(schema);
|
||||||
|
}
|
||||||
let mut registry_ref = &mut registry;
|
|
||||||
let compiled = crate::compiler::Compiler::compile(schema, Some(id.clone()));
|
|
||||||
registry_ref.insert(id, compiled);
|
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -158,6 +143,9 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create Validator Instance (Takes ownership of registry)
|
||||||
|
let validator = Validator::new(registry);
|
||||||
|
|
||||||
// 4. Run Tests
|
// 4. Run Tests
|
||||||
for (_test_index, test) in group.tests.iter().enumerate() {
|
for (_test_index, test) in group.tests.iter().enumerate() {
|
||||||
let mut schema_id = test.schema_id.clone();
|
let mut schema_id = test.schema_id.clone();
|
||||||
@ -193,18 +181,54 @@ pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sid) = schema_id {
|
if let Some(sid) = schema_id {
|
||||||
let result = Validator::validate_with_registry(&sid, &test.data, ®istry);
|
let result = validator.validate(&sid, &test.data);
|
||||||
|
|
||||||
|
let (got_valid, _errors) = match &result {
|
||||||
|
Ok(res) => (res.is_valid(), &res.errors),
|
||||||
|
Err(_e) => {
|
||||||
|
// If we encounter an execution error (e.g. Schema Not Found),
|
||||||
|
// we treat it as a test failure.
|
||||||
|
(false, &vec![])
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(expected) = &test.expected {
|
||||||
|
// Masking Test
|
||||||
|
let mut data_for_mask = test.data.clone();
|
||||||
|
match validator.mask(&sid, &mut data_for_mask) {
|
||||||
|
Ok(_) => {
|
||||||
|
if !equals(&data_for_mask, expected) {
|
||||||
|
let msg = format!(
|
||||||
|
"Masking Test '{}' failed.\nExpected: {:?}\nGot: {:?}",
|
||||||
|
test.description, expected, data_for_mask
|
||||||
|
);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
failures.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let msg = format!(
|
||||||
|
"Masking Test '{}' failed with execution error: {:?}",
|
||||||
|
test.description, e
|
||||||
|
);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
failures.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Standard Validation Test
|
||||||
|
if got_valid != test.valid {
|
||||||
|
let error_msg = match &result {
|
||||||
|
Ok(res) => format!("{:?}", res.errors),
|
||||||
|
Err(e) => format!("Execution Error: {:?}", e),
|
||||||
|
};
|
||||||
|
|
||||||
if !result.errors.is_empty() != !test.valid {
|
|
||||||
failures.push(format!(
|
failures.push(format!(
|
||||||
"[{}] Test '{}' failed. Expected: {}, Got: {}. Errors: {:?}",
|
"[{}] Test '{}' failed. Expected: {}, Got: {}. Errors: {}",
|
||||||
group.description,
|
group.description, test.description, test.valid, got_valid, error_msg
|
||||||
test.description,
|
|
||||||
test.valid,
|
|
||||||
!result.errors.is_empty(), // "Got Invalid?"
|
|
||||||
result.errors
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
failures.push(format!(
|
failures.push(format!(
|
||||||
"[{}] Test '{}' skipped: No schema ID found.",
|
"[{}] Test '{}' skipped: No schema ID found.",
|
||||||
@ -227,8 +251,11 @@ pub fn run_test_file(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
let mut failures = Vec::<String>::new();
|
let mut failures = Vec::<String>::new();
|
||||||
for (group_index, group) in suite.into_iter().enumerate() {
|
for (group_index, group) in suite.into_iter().enumerate() {
|
||||||
|
// Create Isolated Registry for this test group
|
||||||
|
let mut registry = crate::registry::Registry::new();
|
||||||
|
|
||||||
// Helper to register items with 'schemas'
|
// Helper to register items with 'schemas'
|
||||||
let register_schemas = |items_val: Option<Value>| {
|
let register_schemas = |registry: &mut crate::registry::Registry, items_val: Option<Value>| {
|
||||||
if let Some(val) = items_val {
|
if let Some(val) = items_val {
|
||||||
if let Value::Array(arr) = val {
|
if let Value::Array(arr) = val {
|
||||||
for item in arr {
|
for item in arr {
|
||||||
@ -238,14 +265,7 @@ pub fn run_test_file(path: &str) -> Result<(), String> {
|
|||||||
if let Ok(schema) =
|
if let Ok(schema) =
|
||||||
serde_json::from_value::<crate::schema::Schema>(schema_val.clone())
|
serde_json::from_value::<crate::schema::Schema>(schema_val.clone())
|
||||||
{
|
{
|
||||||
// Clone ID upfront to avoid borrow issues
|
registry.add(schema);
|
||||||
if let Some(id_clone) = schema.obj.id.clone() {
|
|
||||||
let mut registry = REGISTRY.write().unwrap();
|
|
||||||
// Utilize the new compile method which handles strictness
|
|
||||||
let compiled =
|
|
||||||
crate::compiler::Compiler::compile(schema, Some(id_clone.clone()));
|
|
||||||
registry.insert(id_clone, compiled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -291,18 +311,16 @@ pub fn run_test_file(path: &str) -> Result<(), String> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if let Ok(schema) = serde_json::from_value::<crate::schema::Schema>(schema_json) {
|
if let Ok(schema) = serde_json::from_value::<crate::schema::Schema>(schema_json) {
|
||||||
let mut registry = REGISTRY.write().unwrap();
|
registry.add(schema);
|
||||||
let compiled = crate::compiler::Compiler::compile(schema, Some(id.clone()));
|
|
||||||
registry.insert(id, compiled);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register 'types', 'enums', and 'puncs' if present (JSPG style)
|
// Register 'types', 'enums', and 'puncs' if present (JSPG style)
|
||||||
register_schemas(group.types);
|
register_schemas(&mut registry, group.types);
|
||||||
register_schemas(group.enums);
|
register_schemas(&mut registry, group.enums);
|
||||||
register_schemas(group.puncs);
|
register_schemas(&mut registry, group.puncs);
|
||||||
|
|
||||||
// Register main 'schema' if present (Standard style)
|
// Register main 'schema' if present (Standard style)
|
||||||
// Ensure ID is a valid URI to avoid Url::parse errors in Compiler
|
// Ensure ID is a valid URI to avoid Url::parse errors in Compiler
|
||||||
@ -310,41 +328,67 @@ pub fn run_test_file(path: &str) -> Result<(), String> {
|
|||||||
|
|
||||||
// Register main 'schema' if present (Standard style)
|
// Register main 'schema' if present (Standard style)
|
||||||
if let Some(ref schema_val) = group.schema {
|
if let Some(ref schema_val) = group.schema {
|
||||||
let mut registry = REGISTRY.write().unwrap();
|
let mut schema: crate::schema::Schema =
|
||||||
let schema: crate::schema::Schema =
|
|
||||||
serde_json::from_value(schema_val.clone()).expect("Failed to parse test schema");
|
serde_json::from_value(schema_val.clone()).expect("Failed to parse test schema");
|
||||||
let compiled = crate::compiler::Compiler::compile(schema, Some(unique_id.clone()));
|
|
||||||
registry.insert(unique_id.clone(), compiled);
|
// If schema has no ID, assign unique_id and use add() or manual insert?
|
||||||
|
// Compiler needs ID. Registry::add needs ID.
|
||||||
|
if schema.obj.id.is_none() {
|
||||||
|
schema.obj.id = Some(unique_id.clone());
|
||||||
}
|
}
|
||||||
|
registry.add(schema);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Instance (Takes Ownership)
|
||||||
|
let validator = Validator::new(registry);
|
||||||
|
|
||||||
for test in group.tests {
|
for test in group.tests {
|
||||||
// Use explicit schema_id from test, or default to unique_id
|
// Use explicit schema_id from test, or default to unique_id
|
||||||
let schema_id = test.schema_id.as_deref().unwrap_or(&unique_id).to_string();
|
let schema_id = test.schema_id.as_deref().unwrap_or(&unique_id).to_string();
|
||||||
|
|
||||||
let drop = Validator::validate(&schema_id, &test.data);
|
let result = validator.validate(&schema_id, &test.data);
|
||||||
|
|
||||||
if test.valid {
|
if test.valid {
|
||||||
if !drop.errors.is_empty() {
|
match result {
|
||||||
|
Ok(res) => {
|
||||||
|
if !res.is_valid() {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Test failed (expected valid): {}\nSchema: {:?}\nData: {:?}\nErrors: {:?}",
|
"Test failed (expected valid): {}\nSchema: {:?}\nData: {:?}\nErrors: {:?}",
|
||||||
test.description,
|
test.description,
|
||||||
group.schema, // We might need to find the actual schema used if schema_id is custom
|
group.schema, // We might need to find the actual schema used if schema_id is custom
|
||||||
test.data,
|
test.data,
|
||||||
drop.errors
|
res.errors
|
||||||
);
|
);
|
||||||
eprintln!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
failures.push(msg);
|
failures.push(msg);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
if drop.errors.is_empty() {
|
Err(e) => {
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"Test failed (expected invalid): {}\nSchema: {:?}\nData: {:?}\nErrors: (Empty)",
|
"Test failed (expected valid) but got execution error: {}\nSchema: {:?}\nData: {:?}\nError: {:?}",
|
||||||
|
test.description, group.schema, test.data, e
|
||||||
|
);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
failures.push(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match result {
|
||||||
|
Ok(res) => {
|
||||||
|
if res.is_valid() {
|
||||||
|
let msg = format!(
|
||||||
|
"Test failed (expected invalid): {}\nSchema: {:?}\nData: {:?}",
|
||||||
test.description, group.schema, test.data
|
test.description, group.schema, test.data
|
||||||
);
|
);
|
||||||
println!("{}", msg);
|
eprintln!("{}", msg);
|
||||||
failures.push(msg);
|
failures.push(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(_) => {
|
||||||
|
// Expected invalid, got error (which implies invalid/failure), so this is PASS.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
574
src/validator.rs
574
src/validator.rs
File diff suppressed because it is too large
Load Diff
171
tests/fixtures/masking.json
vendored
Normal file
171
tests/fixtures/masking.json
vendored
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"description": "Masking Properties",
|
||||||
|
"schema": {
|
||||||
|
"$id": "mask_properties",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"foo": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bar": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"foo"
|
||||||
|
],
|
||||||
|
"extensible": false
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "Keep valid properties",
|
||||||
|
"data": {
|
||||||
|
"foo": "a",
|
||||||
|
"bar": 1
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"foo": "a",
|
||||||
|
"bar": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Remove unknown properties",
|
||||||
|
"data": {
|
||||||
|
"foo": "a",
|
||||||
|
"baz": true
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"foo": "a"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Keep valid properties with unknown",
|
||||||
|
"data": {
|
||||||
|
"foo": "a",
|
||||||
|
"bar": 1,
|
||||||
|
"baz": true
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"foo": "a",
|
||||||
|
"bar": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Masking Nested Objects",
|
||||||
|
"schema": {
|
||||||
|
"$id": "mask_nested",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"meta": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensible": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensible": false
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "Mask nested object",
|
||||||
|
"data": {
|
||||||
|
"meta": {
|
||||||
|
"id": 1,
|
||||||
|
"extra": "x"
|
||||||
|
},
|
||||||
|
"top_extra": "y"
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"meta": {
|
||||||
|
"id": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Masking Arrays",
|
||||||
|
"schema": {
|
||||||
|
"$id": "mask_arrays",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensible": false
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "Arrays are kept (items are valid)",
|
||||||
|
"data": {
|
||||||
|
"tags": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"tags": [
|
||||||
|
"a",
|
||||||
|
"b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Masking Tuple Arrays (prefixItems)",
|
||||||
|
"schema": {
|
||||||
|
"$id": "mask_tuple",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"coord": {
|
||||||
|
"type": "array",
|
||||||
|
"prefixItems": [
|
||||||
|
{
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"extensible": false
|
||||||
|
},
|
||||||
|
"tests": [
|
||||||
|
{
|
||||||
|
"description": "Extra tuple items removed",
|
||||||
|
"data": {
|
||||||
|
"coord": [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
"extra"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"valid": true,
|
||||||
|
"expected": {
|
||||||
|
"coord": [
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1098,6 +1098,30 @@ fn test_pattern_1() {
|
|||||||
util::run_test_file_at_index(&path, 1).unwrap();
|
util::run_test_file_at_index(&path, 1).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masking_0() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
util::run_test_file_at_index(&path, 0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masking_1() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
util::run_test_file_at_index(&path, 1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masking_2() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
util::run_test_file_at_index(&path, 2).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_masking_3() {
|
||||||
|
let path = format!("{}/tests/fixtures/masking.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
util::run_test_file_at_index(&path, 3).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_properties_0() {
|
fn test_max_properties_0() {
|
||||||
let path = format!("{}/tests/fixtures/maxProperties.json", env!("CARGO_MANIFEST_DIR"));
|
let path = format!("{}/tests/fixtures/maxProperties.json", env!("CARGO_MANIFEST_DIR"));
|
||||||
|
|||||||
Reference in New Issue
Block a user