diff --git a/fixtures/database.json b/fixtures/database.json index f3773b6..a64fa9e 100644 --- a/fixtures/database.json +++ b/fixtures/database.json @@ -1,31 +1,62 @@ [ { - "description": "ambiguous M:M and missing boundaries", + "description": "Edge missing - 0 relations", "database": { "types": [ - { "name": "unrelated", "schema": {}, "is_enum": false, "parents": [], "variations": ["unrelated"], "has_table": true }, - { "name": "actor", "schema": {}, "is_enum": false, "parents": [], "variations": ["actor"], "has_table": true }, - { "name": "movie", "schema": {}, "is_enum": false, "parents": [], "variations": ["movie"], "has_table": true }, - { "name": "junction", "schema": {}, "is_enum": false, "parents": [], "variations": ["junction"], "has_table": true } - ], - "relations": [ - { "constraint": "fk_junction_actor1", "source_type": "junction", "destination_type": "actor", "is_array": true, "prefix": null }, - { "constraint": "fk_junction_actor2", "source_type": "junction", "destination_type": "actor", "is_array": true, "prefix": null } - ], - "schemas": [ { - "$id": "get.actor", - "properties": { - "missing_edge": { "$ref": "unrelated" }, - "ambiguous_edge": { "$ref": "junction" } - } + "id": "11111111-1111-1111-1111-111111111111", + "type": "type", + "name": "org", + "module": "test", + "source": "test", + "hierarchy": [ + "org" + ], + "variations": [ + "org" + ], + "schemas": [ + { + "$id": "full.org", + "type": "object", + "properties": { + "missing_users": { + "type": "array", + "items": { + "$ref": "full.user" + } + } + } + } + ] + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "type": "type", + "name": "user", + "module": "test", + "source": "test", + "hierarchy": [ + "user" + ], + "variations": [ + "user" + ], + "schemas": [ + { + "$id": "full.user", + "type": "object", + "properties": {} + } + ] } - ] + ], + "relations": [] }, "tests": [ { - "description": "throws EDGE_MISSING when relation does not exist", - "action": "database_compile", + "description": "throws EDGE_MISSING when 0 relations exist between org and user", + "action": "compile", "expect": { "success": false, "errors": [ @@ -34,10 +65,177 @@ } ] } - }, + } + ] + }, + { + "description": "Edge missing - array cardinality rejection", + "database": { + "types": [ + { + "id": "11111111-1111-1111-1111-111111111111", + "type": "type", + "name": "parent", + "module": "test", + "source": "test", + "hierarchy": [ + "parent" + ], + "variations": [ + "parent" + ], + "schemas": [ + { + "$id": "full.parent", + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "$ref": "full.child" + } + } + } + } + ] + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "type": "type", + "name": "child", + "module": "test", + "source": "test", + "hierarchy": [ + "child" + ], + "variations": [ + "child" + ], + "schemas": [ + { + "$id": "full.child", + "type": "object", + "properties": {} + } + ] + } + ], + "relations": [ + { + "id": "33333333-3333-3333-3333-333333333333", + "type": "relation", + "constraint": "fk_parent_child", + "source_type": "parent", + "source_columns": [ + "child_id" + ], + "destination_type": "child", + "destination_columns": [ + "id" + ] + } + ] + }, + "tests": [ { - "description": "throws AMBIGUOUS_TYPE_RELATIONS when junction has multi null prefixes", - "action": "database_compile", + "description": "throws EDGE_MISSING because a Forward scaler edge cannot mathematically fulfill an Array collection", + "action": "compile", + "expect": { + "success": false, + "errors": [ + { + "code": "EDGE_MISSING" + } + ] + } + } + ] + }, + { + "description": "Ambiguous type relations - multiple unprefixed relations", + "database": { + "types": [ + { + "id": "11111111-1111-1111-1111-111111111111", + "type": "type", + "name": "invoice", + "module": "test", + "source": "test", + "hierarchy": [ + "invoice" + ], + "variations": [ + "invoice" + ], + "schemas": [ + { + "$id": "full.invoice", + "type": "object", + "properties": { + "activities": { + "type": "array", + "items": { + "$ref": "full.activity" + } + } + } + } + ] + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "type": "type", + "name": "activity", + "module": "test", + "source": "test", + "hierarchy": [ + "activity" + ], + "variations": [ + "activity" + ], + "schemas": [ + { + "$id": "full.activity", + "type": "object", + "properties": {} + } + ] + } + ], + "relations": [ + { + "id": "33333333-3333-3333-3333-333333333333", + "type": "relation", + "constraint": "fk_activity_invoice_1", + "source_type": "activity", + "source_columns": [ + "invoice_id_1" + ], + "destination_type": "invoice", + "destination_columns": [ + "id" + ] + }, + { + "id": "44444444-4444-4444-4444-444444444444", + "type": "relation", + "constraint": "fk_activity_invoice_2", + "source_type": "activity", + "source_columns": [ + "invoice_id_2" + ], + "destination_type": "invoice", + "destination_columns": [ + "id" + ] + } + ] + }, + "tests": [ + { + "description": "throws AMBIGUOUS_TYPE_RELATIONS when fallback encounters multiple naked constraints", + "action": "compile", "expect": { "success": false, "errors": [ @@ -50,30 +248,141 @@ ] }, { - "description": "invalid metadata identifiers", + "description": "Ambiguous type relations - M:M twin deduction failure", "database": { - "types": [], - "relations": [], - "schemas": [ + "types": [ { - "$id": "invalid@id", - "properties": {} + "id": "11111111-1111-1111-1111-111111111111", + "type": "type", + "name": "actor", + "module": "test", + "source": "test", + "hierarchy": [ + "actor" + ], + "variations": [ + "actor" + ], + "schemas": [ + { + "$id": "full.actor", + "type": "object", + "properties": { + "ambiguous_edge": { + "type": "array", + "items": { + "$ref": "empty.junction" + } + } + } + } + ] + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "type": "type", + "name": "junction", + "module": "test", + "source": "test", + "hierarchy": [ + "junction" + ], + "variations": [ + "junction" + ], + "schemas": [ + { + "$id": "empty.junction", + "type": "object", + "properties": {} + } + ] + } + ], + "relations": [ + { + "id": "33333333-3333-3333-3333-333333333333", + "type": "relation", + "constraint": "fk_junction_source_actor", + "source_type": "junction", + "source_columns": [ + "source_id" + ], + "destination_type": "actor", + "destination_columns": [ + "id" + ], + "prefix": "source" + }, + { + "id": "44444444-4444-4444-4444-444444444444", + "type": "relation", + "constraint": "fk_junction_target_actor", + "source_type": "junction", + "source_columns": [ + "target_id" + ], + "destination_type": "actor", + "destination_columns": [ + "id" + ], + "prefix": "target" } ] }, "tests": [ { - "description": "throws INVALID_IDENTIFIER for non-alphanumeric ids", - "action": "database_compile", + "description": "throws AMBIGUOUS_TYPE_RELATIONS because child doesn't explicitly expose 'source' or 'target' for twin deduction", + "action": "compile", "expect": { "success": false, "errors": [ { - "code": "INVALID_IDENTIFIER" + "code": "AMBIGUOUS_TYPE_RELATIONS" + } + ] + } + } + ] + }, + { + "description": "Database type parse failed", + "database": { + "types": [ + { + "id": [ + "must", + "be", + "string", + "to", + "fail" + ], + "type": "type", + "name": "failure", + "module": "test", + "source": "test", + "hierarchy": [ + "failure" + ], + "variations": [ + "failure" + ] + } + ] + }, + "tests": [ + { + "description": "throws DATABASE_TYPE_PARSE_FAILED when metadata completely fails Serde typing", + "action": "compile", + "expect": { + "success": false, + "errors": [ + { + "code": "DATABASE_TYPE_PARSE_FAILED" } ] } } ] } -] +] \ No newline at end of file diff --git a/fixtures/emptyString.json b/fixtures/emptyString.json index b7b0a07..0132c48 100644 --- a/fixtures/emptyString.json +++ b/fixtures/emptyString.json @@ -142,7 +142,7 @@ "errors": [ { "code": "CONST_VIOLATED", - "path": "con" + "details": { "path": "con" } } ] } diff --git a/fixtures/families.json b/fixtures/families.json index f800cf4..60d188d 100644 --- a/fixtures/families.json +++ b/fixtures/families.json @@ -155,7 +155,7 @@ "errors": [ { "code": "NO_FAMILY_MATCH", - "path": "" + "details": { "path": "" } } ] } diff --git a/fixtures/merge.json b/fixtures/merge.json index 0a6006e..2631d14 100644 --- a/fixtures/merge.json +++ b/fixtures/merge.json @@ -48,7 +48,7 @@ "errors": [ { "code": "INVALID_TYPE", - "path": "base_prop" + "details": { "path": "base_prop" } } ] } @@ -109,7 +109,7 @@ "errors": [ { "code": "REQUIRED_FIELD_MISSING", - "path": "a" + "details": { "path": "a" } } ] } @@ -126,7 +126,7 @@ "errors": [ { "code": "REQUIRED_FIELD_MISSING", - "path": "b" + "details": { "path": "b" } } ] } @@ -196,7 +196,7 @@ "errors": [ { "code": "DEPENDENCY_MISSING", - "path": "" + "details": { "path": "" } } ] } @@ -214,7 +214,7 @@ "errors": [ { "code": "DEPENDENCY_MISSING", - "path": "" + "details": { "path": "" } } ] } diff --git a/fixtures/paths.json b/fixtures/paths.json index f4ac0b6..287dc6b 100644 --- a/fixtures/paths.json +++ b/fixtures/paths.json @@ -123,7 +123,7 @@ "errors": [ { "code": "INVALID_TYPE", - "path": "primitives/1" + "details": { "path": "primitives/1" } } ] } @@ -147,7 +147,7 @@ "errors": [ { "code": "REQUIRED_FIELD_MISSING", - "path": "ad_hoc_objects/1/name" + "details": { "path": "ad_hoc_objects/1/name" } } ] } @@ -173,7 +173,7 @@ "errors": [ { "code": "MINIMUM_VIOLATED", - "path": "entities/entity-beta/value" + "details": { "path": "entities/entity-beta/value" } } ] } @@ -204,7 +204,7 @@ "errors": [ { "code": "INVALID_TYPE", - "path": "deep_entities/parent-omega/nested/child-beta/flag" + "details": { "path": "deep_entities/parent-omega/nested/child-beta/flag" } } ] } diff --git a/fixtures/ref.json b/fixtures/ref.json index 5014f02..970b472 100644 --- a/fixtures/ref.json +++ b/fixtures/ref.json @@ -677,7 +677,7 @@ "errors": [ { "code": "CONST_VIOLATED", - "path": "type" + "details": { "path": "type" } } ] } @@ -782,7 +782,7 @@ "errors": [ { "code": "CONST_VIOLATED", - "path": "type" + "details": { "path": "type" } } ] } diff --git a/src/tests/fixtures.rs b/src/tests/fixtures.rs index 9b236c0..b51824c 100644 --- a/src/tests/fixtures.rs +++ b/src/tests/fixtures.rs @@ -3479,6 +3479,36 @@ fn test_if_then_else_13_1() { crate::tests::runner::run_test_case(&path, 13, 1).unwrap(); } +#[test] +fn test_database_0_0() { + let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 0, 0).unwrap(); +} + +#[test] +fn test_database_1_0() { + let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 1, 0).unwrap(); +} + +#[test] +fn test_database_2_0() { + let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 2, 0).unwrap(); +} + +#[test] +fn test_database_3_0() { + let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 3, 0).unwrap(); +} + +#[test] +fn test_database_4_0() { + let path = format!("{}/fixtures/database.json", env!("CARGO_MANIFEST_DIR")); + crate::tests::runner::run_test_case(&path, 4, 0).unwrap(); +} + #[test] fn test_empty_string_0_0() { let path = format!("{}/fixtures/emptyString.json", env!("CARGO_MANIFEST_DIR")); diff --git a/src/tests/runner.rs b/src/tests/runner.rs index 829d2a8..c64c683 100644 --- a/src/tests/runner.rs +++ b/src/tests/runner.rs @@ -78,7 +78,7 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<() let mut failures = Vec::::new(); // For validate/merge/query, if setup failed we must structurally fail this test - let db_unwrapped = if test.action.as_str() != "compile" && test.action.as_str() != "database_compile" { + let db_unwrapped = if test.action.as_str() != "compile" { match &**db { Ok(valid_db) => Some(valid_db.clone()), Err(drop) => { @@ -105,7 +105,7 @@ pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<() // 4. Run Tests match test.action.as_str() { - "compile" | "database_compile" => { + "compile" => { let result = test.run_compile(db); if let Err(e) = result { println!("TEST COMPILE ERROR FOR '{}': {}", test.description, e); diff --git a/src/tests/types/expect/drop.rs b/src/tests/types/expect/drop.rs index fb0c839..dfc0299 100644 --- a/src/tests/types/expect/drop.rs +++ b/src/tests/types/expect/drop.rs @@ -50,17 +50,8 @@ fn subset_match(expected: &serde_json::Value, actual: &serde_json::Value) -> boo match (expected, actual) { (serde_json::Value::Object(exp_map), serde_json::Value::Object(act_map)) => { for (k, v) in exp_map { - let mut act_v = act_map.get(k); - - // Transparent fallback: if testing legacy flat "path", gracefully check inside "details" - if act_v.is_none() && k == "path" { - if let Some(serde_json::Value::Object(details)) = act_map.get("details") { - act_v = details.get("path"); - } - } - - if let Some(target) = act_v { - if !subset_match(v, target) { + if let Some(act_v) = act_map.get(k) { + if !subset_match(v, act_v) { return false; } } else {