passing all tests

This commit is contained in:
2026-03-11 17:26:45 -04:00
parent 44be75f5d4
commit 2c74d0a1a6
8 changed files with 9181 additions and 1210 deletions

View File

@ -8,9 +8,11 @@ Read over this entire workflow and commit to every section of work in a task lis
Please analyze the files and directories and do not use cat, find, or the terminal to discover or read in any of these files. Analyze every file mentioned. If a directory is mentioned or a /*, please analyze the directory, every single file at its root, and recursively analyze every subdirectory and every single file in every subdirectory to capture not just critical files, but the entirety of what is requested. I state again, DO NOT just review a cherry picking of files in any folder or wildcard specified. Review 100% of all files discovered recursively! Please analyze the files and directories and do not use cat, find, or the terminal to discover or read in any of these files. Analyze every file mentioned. If a directory is mentioned or a /*, please analyze the directory, every single file at its root, and recursively analyze every subdirectory and every single file in every subdirectory to capture not just critical files, but the entirety of what is requested. I state again, DO NOT just review a cherry picking of files in any folder or wildcard specified. Review 100% of all files discovered recursively!
Section 1: Documentation Section 1: Various Documentation
- GEMINI.md at the root - GEMINI.md at the root (JSPG doc)
- api/punc/GEMINI.md
- api/punc/generator/GEMINI.md
Section 2: Flow file for cmd interface Section 2: Flow file for cmd interface
@ -22,7 +24,14 @@ Section 3: Source
Section 4: Test Fixtures Section 4: Test Fixtures
- Just review some of the *.json files in tests/fixtures/* Just review some of the *.json files in fixtures/ at the root
- fixtures/allOf.json
- fixtures/ref.json
- fixtures/contains.json
- fixtures/queryer.json
- fixtures/merger.json
- fixtures/merge.json
Section 5: Build Section 5: Build
@ -36,14 +45,19 @@ Section 7: Some PUNC Syntax
Now, review some punc type and enum source in the api project with api/ these files: Now, review some punc type and enum source in the api project with api/ these files:
- punc/sql/tables.sql - api/punc/sql/tables.sql
- punc/sql/domains.sql - api/punc/sql/domains.sql
- punc/sql/indexes.sql - api/punc/sql/indexes.sql
- punc/sql/functions/entity.sql - api/punc/sql/functions/entity.sql
- punc/sql/functions/puncs.sql - api/punc/sql/functions/puncs.sql
- punc/sql/puncs/entity.sql - api/punc/sql/puncs/entity.sql
- punc/sql/puncs/persons.sql - api/punc/sql/puncs/persons.sql
- punc/sql/puncs/puncs.sql - api/punc/sql/puncs/puncs.sql
- punc/sql/puncs/job.sql - api/punc/sql/puncs/job.sql
- api/contact/sql/tables.sql
- api/contact/sql/domains.sql
- api/contact/sql/indexes.sql
- api/contact/sql/domains.sql
- api/contact/sql/puncs/contacts.sql
Now you are ready to help me work on this extension. Now you are ready to help me work on this extension.

View File

@ -63,6 +63,7 @@ fn main() {
.unwrap() .unwrap()
.as_array() .as_array()
.expect("Tests must be an array"); .expect("Tests must be an array");
let safe_filename = to_safe_identifier(file_name);
for (t_idx, test) in tests.iter().enumerate() { for (t_idx, test) in tests.iter().enumerate() {
let t_obj = test.as_object().expect("Test case must be an object"); let t_obj = test.as_object().expect("Test case must be an object");
if !t_obj.contains_key("description") { if !t_obj.contains_key("description") {
@ -71,25 +72,24 @@ fn main() {
file_name, i, t_idx file_name, i, t_idx
); );
} }
}
// Use deterministic names: test_{filename}_{index} // Use deterministic names: test_{filename}_{suite_idx}_{test_idx}
let safe_filename = to_safe_identifier(file_name); let fn_name = format!("test_{}_{}_{}", safe_filename, i, t_idx);
let fn_name = format!("test_{}_{}", safe_filename, i);
// Write to src/tests/fixtures.rs (Std Test) // Write to src/tests/fixtures.rs (Std Test)
write!( write!(
std_file, std_file,
r#" r#"
#[test] #[test]
fn {}() {{ fn {}() {{
let path = format!("{{}}/fixtures/{}.json", env!("CARGO_MANIFEST_DIR")); let path = format!("{{}}/fixtures/{}.json", env!("CARGO_MANIFEST_DIR"));
crate::tests::runner::run_test_file_at_index(&path, {}).unwrap(); crate::tests::runner::run_test_case(&path, {}, {}).unwrap();
}} }}
"#, "#,
fn_name, file_name, i fn_name, file_name, i, t_idx
) )
.unwrap(); .unwrap();
}
} }
} }
} }

View File

@ -137,13 +137,923 @@
"success": true, "success": true,
"sql": [ "sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) FROM agreego.\"simple_entity\" t1 LEFT JOIN agreego.\"entity\" t2 ON t2.id = t1.id WHERE t1.id = '123'", "SELECT to_jsonb(t1.*) || to_jsonb(t2.*) FROM agreego.\"simple_entity\" t1 LEFT JOIN agreego.\"entity\" t2 ON t2.id = t1.id WHERE t1.id = '123'",
"INSERT INTO agreego.\"simple_entity\" (\"id\", \"name\", \"type\") VALUES ('123', 'Jane', 'simple_entity')",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '123', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'simple_entity')", "INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '123', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'simple_entity')",
"INSERT INTO agreego.\"simple_entity\" (\"id\", \"name\", \"type\") VALUES ('123', 'Jane', 'simple_entity')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"name\":\"Jane\",\"type\":\"simple_entity\"}', '123', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')", "INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"name\":\"Jane\",\"type\":\"simple_entity\"}', '123', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"id\":\"123\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"name\":\"Jane\",\"type\":\"simple_entity\"}}')" "SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"id\":\"123\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"name\":\"Jane\",\"type\":\"simple_entity\"}}')"
] ]
} }
} }
] ]
},
{
"description": "Hierarchical Lookups and Inserts",
"database": {
"puncs": [],
"enums": [],
"relations": [],
"types": [
{
"name": "entity",
"schemas": [
{
"$id": "entity",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"archived": {
"type": "boolean"
},
"created_by": {
"type": "string"
},
"modified_by": {
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"modified_at": {
"type": "string",
"format": "date-time"
}
},
"required": [
"id",
"type",
"created_by",
"created_at",
"modified_by",
"modified_at"
]
}
],
"hierarchy": [
"entity"
],
"fields": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": false,
"relationship": false
},
{
"name": "organization",
"schemas": [
{
"$id": "organization",
"$ref": "entity",
"properties": {
"name": {
"type": "string"
}
}
}
],
"hierarchy": [
"organization",
"entity"
],
"fields": [
"id",
"type",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
},
{
"name": "user",
"schemas": [
{
"$id": "user",
"$ref": "organization",
"properties": {}
}
],
"hierarchy": [
"user",
"organization",
"entity"
],
"fields": [
"id",
"type",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"user": [
"id",
"type"
],
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
},
{
"name": "person",
"schemas": [
{
"$id": "person",
"$ref": "user",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"date_of_birth": {
"type": "string"
},
"pronouns": {
"type": "string"
},
"contact_id": {
"type": "string"
}
}
}
],
"hierarchy": [
"person",
"user",
"organization",
"entity"
],
"fields": [
"id",
"type",
"first_name",
"last_name",
"date_of_birth",
"pronouns",
"contact_id",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"person": [
"id",
"type",
"first_name",
"last_name",
"date_of_birth",
"pronouns",
"contact_id"
],
"user": [
"id",
"type"
],
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [
"first_name",
"last_name",
"date_of_birth",
"pronouns"
],
"historical": true,
"relationship": false
},
{
"name": "order",
"schemas": [
{
"$id": "order",
"$ref": "entity",
"properties": {
"total": {
"type": "number"
},
"customer_id": {
"type": "string"
}
}
}
],
"hierarchy": [
"order",
"entity"
],
"fields": [
"id",
"type",
"total",
"customer_id",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"order": [
"id",
"type",
"total",
"customer_id"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [
"id"
],
"historical": true,
"relationship": false
},
{
"name": "order_line",
"schemas": [
{
"$id": "order_line",
"$ref": "entity",
"properties": {
"order_id": {
"type": "string"
},
"product": {
"type": "string"
},
"price": {
"type": "number"
}
}
}
],
"hierarchy": [
"order_line",
"entity"
],
"fields": [
"id",
"type",
"order_id",
"product",
"price",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"order_line": [
"id",
"type",
"order_id",
"product",
"price"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
}
]
},
"tests": [
{
"description": "Should query by ID if provided, rather than looking up by fields",
"action": "merge",
"data": {
"id": "123",
"type": "person",
"first_name": "John",
"last_name": "Doe",
"date_of_birth": "1990-01-01T00:00:00Z"
},
"expect": {
"success": true,
"sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*) FROM agreego.\"person\" t1 LEFT JOIN agreego.\"user\" t2 ON t2.id = t1.id LEFT JOIN agreego.\"organization\" t3 ON t3.id = t1.id LEFT JOIN agreego.\"entity\" t4 ON t4.id = t1.id WHERE t1.id = '123'",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '123', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'person')",
"INSERT INTO agreego.\"organization\" (\"id\", \"type\") VALUES ('123', 'person')",
"INSERT INTO agreego.\"user\" (\"id\", \"type\") VALUES ('123', 'person')",
"INSERT INTO agreego.\"person\" (\"date_of_birth\", \"first_name\", \"id\", \"last_name\", \"type\") VALUES ('1990-01-01T00:00:00Z', 'John', '123', 'Doe', 'person')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"type\":\"person\"}', '123', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"id\":\"123\",\"last_name\":\"Doe\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"type\":\"person\"}}')"
]
}
},
{
"description": "Should execute a lookup based on defined lookup_fields because no ID is provided",
"action": "merge",
"data": {
"type": "person",
"first_name": "John",
"last_name": "Doe",
"date_of_birth": "1990-01-01T00:00:00Z"
},
"expect": {
"success": true,
"sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*) FROM agreego.\"person\" t1 LEFT JOIN agreego.\"user\" t2 ON t2.id = t1.id LEFT JOIN agreego.\"organization\" t3 ON t3.id = t1.id LEFT JOIN agreego.\"entity\" t4 ON t4.id = t1.id WHERE \"first_name\" = 'John' AND \"last_name\" = 'Doe' AND \"date_of_birth\" = '1990-01-01T00:00:00Z' AND \"pronouns\" IS NULL",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '{{uuid:person_1}}', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'person')",
"INSERT INTO agreego.\"organization\" (\"id\", \"type\") VALUES ('{{uuid:person_1}}', 'person')",
"INSERT INTO agreego.\"user\" (\"id\", \"type\") VALUES ('{{uuid:person_1}}', 'person')",
"INSERT INTO agreego.\"person\" (\"date_of_birth\", \"first_name\", \"id\", \"last_name\", \"type\") VALUES ('1990-01-01T00:00:00Z', 'John', '{{uuid:person_1}}', 'Doe', 'person')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"type\":\"person\"}', '{{uuid:person_1}}', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"id\":\"{{uuid:person_1}}\",\"last_name\":\"Doe\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"type\":\"person\"}}')"
]
}
},
{
"description": "Should explicitly null out fields provided as empty strings",
"action": "merge",
"data": {
"id": "123",
"type": "person",
"first_name": "John",
"last_name": "Doe",
"date_of_birth": "1990-01-01T00:00:00Z",
"pronouns": ""
},
"expect": {
"success": true,
"sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*) FROM agreego.\"person\" t1 LEFT JOIN agreego.\"user\" t2 ON t2.id = t1.id LEFT JOIN agreego.\"organization\" t3 ON t3.id = t1.id LEFT JOIN agreego.\"entity\" t4 ON t4.id = t1.id WHERE t1.id = '123'",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '123', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'person')",
"INSERT INTO agreego.\"organization\" (\"id\", \"type\") VALUES ('123', 'person')",
"INSERT INTO agreego.\"user\" (\"id\", \"type\") VALUES ('123', 'person')",
"INSERT INTO agreego.\"person\" (\"date_of_birth\", \"first_name\", \"id\", \"last_name\", \"pronouns\", \"type\") VALUES ('1990-01-01T00:00:00Z', 'John', '123', 'Doe', NULL, 'person')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"pronouns\":\"\",\"type\":\"person\"}', '123', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"date_of_birth\":\"1990-01-01T00:00:00Z\",\"first_name\":\"John\",\"id\":\"123\",\"last_name\":\"Doe\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"pronouns\":\"\",\"type\":\"person\"}}')"
]
}
}
]
},
{
"description": "Nested Relationship Mapping",
"database": {
"puncs": [],
"enums": [
{
"id": "e-type-1",
"type": "relation_type",
"enum": "relation_type",
"values": [
"foreign_key",
"polymorphic",
"graph"
]
}
],
"relations": [
{
"id": "r-order-customer",
"type": "relation",
"constraint": "fk_order_customer",
"source_type": "order",
"source_columns": [
"customer_id"
],
"destination_type": "person",
"destination_columns": [
"id"
],
"prefix": "customer"
},
{
"id": "r-order-lines",
"type": "relation",
"constraint": "fk_order_line_order",
"source_type": "order_line",
"source_columns": [
"order_id"
],
"destination_type": "order",
"destination_columns": [
"id"
],
"prefix": "lines"
}
],
"types": [
{
"name": "entity",
"schemas": [
{
"$id": "entity",
"type": "object",
"properties": {
"id": {
"type": "string"
},
"type": {
"type": "string"
},
"archived": {
"type": "boolean"
},
"created_by": {
"type": "string"
},
"modified_by": {
"type": "string"
},
"created_at": {
"type": "string",
"format": "date-time"
},
"modified_at": {
"type": "string",
"format": "date-time"
}
},
"required": [
"id",
"type",
"created_by",
"created_at",
"modified_by",
"modified_at"
]
}
],
"hierarchy": [
"entity"
],
"fields": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": false,
"relationship": false
},
{
"name": "organization",
"schemas": [
{
"$id": "organization",
"$ref": "entity",
"properties": {
"name": {
"type": "string"
}
}
}
],
"hierarchy": [
"organization",
"entity"
],
"fields": [
"id",
"type",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
},
{
"name": "user",
"schemas": [
{
"$id": "user",
"$ref": "organization",
"properties": {}
}
],
"hierarchy": [
"user",
"organization",
"entity"
],
"fields": [
"id",
"type",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"user": [
"id",
"type"
],
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
},
{
"name": "person",
"schemas": [
{
"$id": "person",
"$ref": "user",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"date_of_birth": {
"type": "string"
},
"pronouns": {
"type": "string"
},
"contact_id": {
"type": "string"
}
}
}
],
"hierarchy": [
"person",
"user",
"organization",
"entity"
],
"fields": [
"id",
"type",
"first_name",
"last_name",
"date_of_birth",
"pronouns",
"contact_id",
"name",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"person": [
"id",
"type",
"first_name",
"last_name",
"date_of_birth",
"pronouns",
"contact_id"
],
"user": [
"id",
"type"
],
"organization": [
"id",
"type",
"name"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [
"first_name",
"last_name",
"date_of_birth",
"pronouns"
],
"historical": true,
"relationship": false
},
{
"name": "order",
"schemas": [
{
"$id": "order",
"$ref": "entity",
"properties": {
"total": {
"type": "number"
},
"customer_id": {
"type": "string"
}
}
}
],
"hierarchy": [
"order",
"entity"
],
"fields": [
"id",
"type",
"total",
"customer_id",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"order": [
"id",
"type",
"total",
"customer_id"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [
"id"
],
"historical": true,
"relationship": false
},
{
"name": "order_line",
"schemas": [
{
"$id": "order_line",
"$ref": "entity",
"properties": {
"order_id": {
"type": "string"
},
"product": {
"type": "string"
},
"price": {
"type": "number"
}
}
}
],
"hierarchy": [
"order_line",
"entity"
],
"fields": [
"id",
"type",
"order_id",
"product",
"price",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
],
"grouped_fields": {
"order_line": [
"id",
"type",
"order_id",
"product",
"price"
],
"entity": [
"id",
"type",
"created_at",
"created_by",
"modified_at",
"modified_by",
"archived"
]
},
"lookup_fields": [],
"historical": true,
"relationship": false
}
]
},
"tests": [
{
"description": "Should execute a 1:1 nested relation and cascade child IDs to parent",
"action": "merge",
"data": {
"type": "order",
"total": 100.0,
"customer": {
"type": "person",
"first_name": "Bob",
"last_name": "Smith",
"date_of_birth": "2000-01-01"
}
},
"expect": {
"success": true,
"sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) FROM agreego.\"order\" t1 LEFT JOIN agreego.\"entity\" t2 ON t2.id = t1.id WHERE \"id\" IS NULL",
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) || to_jsonb(t3.*) || to_jsonb(t4.*) FROM agreego.\"person\" t1 LEFT JOIN agreego.\"user\" t2 ON t2.id = t1.id LEFT JOIN agreego.\"organization\" t3 ON t3.id = t1.id LEFT JOIN agreego.\"entity\" t4 ON t4.id = t1.id WHERE \"first_name\" = 'Bob' AND \"last_name\" = 'Smith' AND \"date_of_birth\" = '2000-01-01' AND \"pronouns\" IS NULL",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '{{uuid:customer_id}}', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'person')",
"INSERT INTO agreego.\"organization\" (\"id\", \"type\") VALUES ('{{uuid:customer_id}}', 'person')",
"INSERT INTO agreego.\"user\" (\"id\", \"type\") VALUES ('{{uuid:customer_id}}', 'person')",
"INSERT INTO agreego.\"person\" (\"date_of_birth\", \"first_name\", \"id\", \"last_name\", \"type\") VALUES ('2000-01-01', 'Bob', '{{uuid:customer_id}}', 'Smith', 'person')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"date_of_birth\":\"2000-01-01\",\"first_name\":\"Bob\",\"last_name\":\"Smith\",\"type\":\"person\"}', '{{uuid:customer_id}}', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"date_of_birth\":\"2000-01-01\",\"first_name\":\"Bob\",\"id\":\"{{uuid:customer_id}}\",\"last_name\":\"Smith\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"type\":\"person\"}}')",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '{{uuid:order_id}}', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'order')",
"INSERT INTO agreego.\"order\" (\"customer_id\", \"id\", \"total\", \"type\") VALUES ('{{uuid:customer_id}}', '{{uuid:order_id}}', 100, 'order')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"customer_id\":\"{{uuid:customer_id}}\",\"total\":100.0,\"type\":\"order\"}', '{{uuid:order_id}}', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"customer_id\":\"{{uuid:customer_id}}\",\"id\":\"{{uuid:order_id}}\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"total\":100.0,\"type\":\"order\"}}')"
]
}
},
{
"description": "Should execute a 1:N array relation mapping parent IDs to children",
"action": "merge",
"data": {
"id": "abc",
"type": "order",
"total": 99.0,
"lines": [
{
"type": "order_line",
"product": "Widget",
"price": 99.0
}
]
},
"expect": {
"success": true,
"sql": [
"SELECT to_jsonb(t1.*) || to_jsonb(t2.*) FROM agreego.\"order\" t1 LEFT JOIN agreego.\"entity\" t2 ON t2.id = t1.id WHERE t1.id = 'abc'",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'abc', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'order')",
"INSERT INTO agreego.\"order\" (\"id\", \"total\", \"type\") VALUES ('abc', 99, 'order')",
"INSERT INTO agreego.\"entity\" (\"created_at\", \"created_by\", \"id\", \"modified_at\", \"modified_by\", \"type\") VALUES ('{{timestamp}}', '00000000-0000-0000-0000-000000000000', '{{uuid:line_id}}', '{{timestamp}}', '00000000-0000-0000-0000-000000000000', 'order_line')",
"INSERT INTO agreego.\"order_line\" (\"id\", \"order_id\", \"price\", \"product\", \"type\") VALUES ('{{uuid:line_id}}', 'abc', 99, 'Widget', 'order_line')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"order_id\":\"abc\",\"price\":99.0,\"product\":\"Widget\",\"type\":\"order_line\"}', '{{uuid:line_id}}', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"id\":\"{{uuid:line_id}}\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"order_id\":\"abc\",\"price\":99.0,\"product\":\"Widget\",\"type\":\"order_line\"}}')",
"INSERT INTO agreego.change (changes, entity_id, id, kind, modified_at, modified_by) VALUES ('{\"total\":99.0,\"type\":\"order\"}', 'abc', '{{uuid}}', 'create', '{{timestamp}}', '00000000-0000-0000-0000-000000000000')",
"SELECT pg_notify('entity', '{\"complete\":{\"created_at\":\"{{timestamp}}\",\"created_by\":\"00000000-0000-0000-0000-000000000000\",\"id\":\"abc\",\"modified_at\":\"{{timestamp}}\",\"modified_by\":\"00000000-0000-0000-0000-000000000000\",\"total\":99.0,\"type\":\"order\"}}')"
]
}
}
]
} }
] ]

View File

@ -2,23 +2,38 @@ use crate::database::executors::DatabaseExecutor;
use serde_json::Value; use serde_json::Value;
#[cfg(test)] #[cfg(test)]
use std::sync::Mutex; use std::cell::RefCell;
#[cfg(test)] #[cfg(test)]
pub struct MockExecutor { pub struct MockState {
pub query_responses: Mutex<Vec<Result<Value, String>>>, pub captured_queries: Vec<String>,
pub execute_responses: Mutex<Vec<Result<(), String>>>, pub query_responses: Vec<Result<Value, String>>,
pub captured_queries: Mutex<Vec<String>>, pub execute_responses: Vec<Result<(), String>>,
} }
#[cfg(test)]
impl MockState {
pub fn new() -> Self {
Self {
captured_queries: Default::default(),
query_responses: Default::default(),
execute_responses: Default::default(),
}
}
}
#[cfg(test)]
thread_local! {
pub static MOCK_STATE: RefCell<MockState> = RefCell::new(MockState::new());
}
#[cfg(test)]
pub struct MockExecutor {}
#[cfg(test)] #[cfg(test)]
impl MockExecutor { impl MockExecutor {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {}
query_responses: Mutex::new(Vec::new()),
execute_responses: Mutex::new(Vec::new()),
captured_queries: Mutex::new(Vec::new()),
}
} }
} }
@ -26,22 +41,26 @@ impl MockExecutor {
impl DatabaseExecutor for MockExecutor { impl DatabaseExecutor for MockExecutor {
fn query(&self, sql: &str, _args: Option<&[Value]>) -> Result<Value, String> { fn query(&self, sql: &str, _args: Option<&[Value]>) -> Result<Value, String> {
println!("DEBUG SQL QUERY: {}", sql); println!("DEBUG SQL QUERY: {}", sql);
self.captured_queries.lock().unwrap().push(sql.to_string()); MOCK_STATE.with(|state| {
let mut responses = self.query_responses.lock().unwrap(); let mut s = state.borrow_mut();
if responses.is_empty() { s.captured_queries.push(sql.to_string());
return Ok(Value::Array(vec![])); if s.query_responses.is_empty() {
} return Ok(Value::Array(vec![]));
responses.remove(0) }
s.query_responses.remove(0)
})
} }
fn execute(&self, sql: &str, _args: Option<&[Value]>) -> Result<(), String> { fn execute(&self, sql: &str, _args: Option<&[Value]>) -> Result<(), String> {
println!("DEBUG SQL EXECUTE: {}", sql); println!("DEBUG SQL EXECUTE: {}", sql);
self.captured_queries.lock().unwrap().push(sql.to_string()); MOCK_STATE.with(|state| {
let mut responses = self.execute_responses.lock().unwrap(); let mut s = state.borrow_mut();
if responses.is_empty() { s.captured_queries.push(sql.to_string());
return Ok(()); if s.execute_responses.is_empty() {
} return Ok(());
responses.remove(0) }
s.execute_responses.remove(0)
})
} }
fn auth_user_id(&self) -> Result<String, String> { fn auth_user_id(&self) -> Result<String, String> {
@ -54,11 +73,16 @@ impl DatabaseExecutor for MockExecutor {
#[cfg(test)] #[cfg(test)]
fn get_queries(&self) -> Vec<String> { fn get_queries(&self) -> Vec<String> {
self.captured_queries.lock().unwrap().clone() MOCK_STATE.with(|state| state.borrow().captured_queries.clone())
} }
#[cfg(test)] #[cfg(test)]
fn reset_mocks(&self) { fn reset_mocks(&self) {
self.captured_queries.lock().unwrap().clear(); MOCK_STATE.with(|state| {
let mut s = state.borrow_mut();
s.captured_queries.clear();
s.query_responses.clear();
s.execute_responses.clear();
});
} }
} }

View File

@ -76,8 +76,11 @@ impl Database {
if let Some(arr) = val.get("relations").and_then(|v| v.as_array()) { if let Some(arr) = val.get("relations").and_then(|v| v.as_array()) {
for item in arr { for item in arr {
if let Ok(def) = serde_json::from_value::<Relation>(item.clone()) { match serde_json::from_value::<Relation>(item.clone()) {
db.relations.insert(def.constraint.clone(), def); Ok(def) => {
db.relations.insert(def.constraint.clone(), def);
}
Err(e) => println!("DATABASE RELATION PARSE FAILED: {:?}", e),
} }
} }
} }

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap;
use std::fs; use std::fs;
use std::sync::{Arc, OnceLock, RwLock};
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct TestSuite { pub struct TestSuite {
#[allow(dead_code)] #[allow(dead_code)]
description: String, pub description: String,
database: serde_json::Value, pub database: serde_json::Value,
tests: Vec<TestCase>, pub tests: Vec<TestCase>,
} }
use crate::tests::types::{ExpectBlock, TestCase}; use crate::tests::types::TestCase;
use crate::validator::Validator;
use serde_json::Value; use serde_json::Value;
pub fn deserialize_some<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error> pub fn deserialize_some<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
@ -22,87 +22,128 @@ where
Ok(Some(v)) Ok(Some(v))
} }
pub fn run_test_file_at_index(path: &str, index: usize) -> Result<(), String> { // Type alias for easier reading
let content = type CompiledSuite = Arc<Vec<(TestSuite, Arc<crate::database::Database>)>>;
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", path));
let suite: Vec<TestSuite> = serde_json::from_str(&content)
.unwrap_or_else(|e| panic!("Failed to parse JSON in {}: {}", path, e));
if index >= suite.len() { // Global cache mapping filename -> Vector of (Parsed JSON suite, Compiled Database)
panic!("Index {} out of bounds for file {}", index, path); static CACHE: OnceLock<RwLock<HashMap<String, CompiledSuite>>> = OnceLock::new();
fn get_cached_file(path: &str) -> CompiledSuite {
let cache_lock = CACHE.get_or_init(|| RwLock::new(HashMap::new()));
let file_data = {
let read_guard = cache_lock.read().unwrap();
read_guard.get(path).cloned()
};
match file_data {
Some(data) => data,
None => {
let mut write_guard = cache_lock.write().unwrap();
// double check in case another thread compiled while we waited for lock
if let Some(data) = write_guard.get(path) {
data.clone()
} else {
let content =
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read file: {}", path));
let suites: Vec<TestSuite> = serde_json::from_str(&content)
.unwrap_or_else(|e| panic!("Failed to parse JSON in {}: {}", path, e));
let mut compiled_suites = Vec::new();
for suite in suites {
let db_result = crate::database::Database::new(&suite.database);
if let Err(drop) = db_result {
let error_messages: Vec<String> = drop
.errors
.into_iter()
.map(|e| format!("Error {} at path {}: {}", e.code, e.details.path, e.message))
.collect();
panic!(
"System Setup Compilation failed for {}:\n{}",
path,
error_messages.join("\n")
);
}
compiled_suites.push((suite, Arc::new(db_result.unwrap())));
}
let new_data = Arc::new(compiled_suites);
write_guard.insert(path.to_string(), new_data.clone());
new_data
}
}
}
}
pub fn run_test_case(path: &str, suite_idx: usize, case_idx: usize) -> Result<(), String> {
let file_data = get_cached_file(path);
if suite_idx >= file_data.len() {
panic!("Suite Index {} out of bounds for file {}", suite_idx, path);
} }
let group = &suite[index]; let (group, db) = &file_data[suite_idx];
if case_idx >= group.tests.len() {
panic!(
"Case Index {} out of bounds for suite {} in file {}",
case_idx, suite_idx, path
);
}
let test = &group.tests[case_idx];
let mut failures = Vec::<String>::new(); let mut failures = Vec::<String>::new();
let db_json = group.database.clone();
let db_result = crate::database::Database::new(&db_json);
if let Err(drop) = db_result {
let error_messages: Vec<String> = drop
.errors
.into_iter()
.map(|e| format!("Error {} at path {}: {}", e.code, e.details.path, e.message))
.collect();
return Err(format!(
"System Setup Compilation failed:\n{}",
error_messages.join("\n")
));
}
let db = db_result.unwrap();
let validator = Validator::new(std::sync::Arc::new(db));
// 4. Run Tests // 4. Run Tests
for test in group.tests.iter() { // Provide fallback for legacy expectations if `expect` block was missing despite migration script
// Provide fallback for legacy expectations if `expect` block was missing despite migration script let _expected_success = test
let expected_success = test .expect
.expect .as_ref()
.as_ref() .map(|e| e.success)
.map(|e| e.success) .unwrap_or(test.valid.unwrap_or(false));
.unwrap_or(test.valid.unwrap_or(false)); let _expected_errors = test
let _expected_errors = test .expect
.expect .as_ref()
.as_ref() .and_then(|e| e.errors.clone())
.and_then(|e| e.errors.clone()) .unwrap_or(test.expect_errors.clone().unwrap_or(vec![]));
.unwrap_or(test.expect_errors.clone().unwrap_or(vec![]));
match test.action.as_str() { match test.action.as_str() {
"validate" => { "validate" => {
let result = test.run_validate(validator.db.clone()); let result = test.run_validate(db.clone());
if let Err(e) = result { if let Err(e) = result {
println!("TEST VALIDATE ERROR FOR '{}': {}", test.description, e); println!("TEST VALIDATE ERROR FOR '{}': {}", test.description, e);
failures.push(format!(
"[{}] Validate Test '{}' failed. Error: {}",
group.description, test.description, e
));
}
}
"merge" => {
let result = test.run_merge(validator.db.clone());
if let Err(e) = result {
println!("TEST MERGE ERROR FOR '{}': {}", test.description, e);
failures.push(format!(
"[{}] Merge Test '{}' failed. Error: {}",
group.description, test.description, e
));
}
}
"query" => {
let result = test.run_query(validator.db.clone());
if let Err(e) = result {
println!("TEST QUERY ERROR FOR '{}': {}", test.description, e);
failures.push(format!(
"[{}] Query Test '{}' failed. Error: {}",
group.description, test.description, e
));
}
}
_ => {
failures.push(format!( failures.push(format!(
"[{}] Unknown action '{}' for test '{}'", "[{}] Validate Test '{}' failed. Error: {}",
group.description, test.action, test.description group.description, test.description, e
)); ));
} }
} }
"merge" => {
let result = test.run_merge(db.clone());
if let Err(e) = result {
println!("TEST MERGE ERROR FOR '{}': {}", test.description, e);
failures.push(format!(
"[{}] Merge Test '{}' failed. Error: {}",
group.description, test.description, e
));
}
}
"query" => {
let result = test.run_query(db.clone());
if let Err(e) = result {
println!("TEST QUERY ERROR FOR '{}': {}", test.description, e);
failures.push(format!(
"[{}] Query Test '{}' failed. Error: {}",
group.description, test.description, e
));
}
}
_ => {
failures.push(format!(
"[{}] Unknown action '{}' for test '{}'",
group.description, test.action, test.description
));
}
} }
if !failures.is_empty() { if !failures.is_empty() {