Skip to content

Commit 37e1f41

Browse files
dmealingclaude
andcommitted
test(conformance): shared #195 origin-validation error-fixtures (10)
Issue #195's projection read-model origins (origin.computed @expr, origin.aggregate @agg:any|all|collect, origin.first) added loader validation rules covered only by per-port LOCAL unit tests — no shared cross-port conformance fixture gated them. Add 10 single-violation error-fixtures under fixtures/conformance/error-* so all four independent loaders (TS/C#/Python/Java; Kotlin via the Java loader) assert byte-identical error code + jsonPath: error-computed-type-mismatch ERR_COMPUTED_TYPE_MISMATCH error-computed-unknown-expr-node ERR_UNKNOWN_EXPR_NODE error-computed-op-illegal-operand ERR_INVALID_ORIGIN error-computed-unresolved-field ERR_INVALID_ORIGIN error-agg-collect-element-type-mismatch ERR_INVALID_ORIGIN error-agg-collect-non-array ERR_INVALID_ORIGIN error-agg-all-non-boolean ERR_INVALID_ORIGIN error-agg-any-missing-filter ERR_INVALID_ORIGIN error-first-of-type-mismatch ERR_INVALID_ORIGIN error-first-required-field ERR_INVALID_ORIGIN Each is a one-token mutation of an existing green positive twin (origin-computed-isnotnull / origin-agg-collect / origin-agg-all / origin-first-latest), so exactly one rule trips. All four loaders already agreed — this locks the agreement in (no divergence found, no code change). Also refresh the stale ERROR-CODES.json ERR_INVALID_ORIGIN description (post-#195 it also covers computed op-legality/inference and origin.first rules). Test-only; no library behavior change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NGQ7oSuNcjhsMHWwZzhBwr
1 parent e78bbce commit 37e1f41

31 files changed

Lines changed: 646 additions & 1 deletion

File tree

fixtures/conformance/ERROR-CODES.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"ERR_SUBTYPE_RULE_VIOLATION": "A node violates a subtype composition rule (e.g. a value object with an identity or a source — FR-024 value purity; a projection extending a non-projection — FR-024/ADR-0028).",
2828
"ERR_OVERLAY_NO_TARGET": "An overlay node has no existing target to merge into.",
2929
"ERR_MALFORMED_YAML": "The YAML metadata input is not valid YAML, or cannot be desugared into canonical metadata.",
30-
"ERR_INVALID_ORIGIN": "A field origin (passthrough/aggregate) declares an invalid path or attribute.",
30+
"ERR_INVALID_ORIGIN": "A field origin declares an invalid path or attribute. Covers passthrough/aggregate path + attribute violations, and (post-#195) origin.computed @expr op-legality/inference failures (e.g. an op illegal for an operand subtype, or a field ref that does not resolve on the derived base entity) and every origin.first rule (missing/unresolvable @of or @orderBy, @of type-preservation, first-on-@required). The dedicated computed codes ERR_UNKNOWN_EXPR_NODE (unknown @expr node/op/fn) and ERR_COMPUTED_TYPE_MISMATCH (@expr root type != field subType) are raised separately.",
3131
"ERR_AMBIGUOUS_PATH": "FR-024 (ADR-0029): an implicit (omitted-@via) origin path is ambiguous — more than one single-hop relationship leads from the base entity to the @from/@of entity (the error names the candidates), or a projection's base entity cannot be derived because its fields extend multiple entities and no extended identity anchors the base.",
3232
"ERR_ORIGIN_CARDINALITY": "FR-024 (ADR-0029): origin cardinality contract broken — a passthrough @via path crosses a to-many hop (row-multiplying passthrough — you meant aggregate), or an aggregate @via path is to-one at every hop (aggregating over a to-one path — you meant passthrough). Checked on explicit AND inferred paths.",
3333
"ERR_EXTENDS_ORIGIN_MISMATCH": "FR-024 (ADR-0029 decision 7): a field declares both an entity-nested extends (shape lineage) and an origin.passthrough @from (data lineage) and they disagree — the resolved @from target is not the field's resolved extends target (nor anywhere on its extends chain). Host-agnostic (projections, entities, values). origin.aggregate is never judged (it computes something new); a top-level abstract extends target is never judged (shape-only reuse makes no lineage claim).",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"errors": [
3+
{
4+
"code": "ERR_INVALID_ORIGIN",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.sessions.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[2]['object.projection'].children[2]['field.string'].children[0]['origin.aggregate']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::sessions",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Session",
8+
"children": [
9+
{ "source.rdb": { "@table": "sessions" } },
10+
{ "field.long": { "name": "id" } },
11+
{ "relationship.association": { "name": "turns", "@objectRef": "acme::sessions::Turn", "@cardinality": "many" } },
12+
{ "identity.primary": { "name": "id", "@fields": "id" } }
13+
]
14+
}
15+
},
16+
{
17+
"object.entity": {
18+
"name": "Turn",
19+
"children": [
20+
{ "source.rdb": { "@table": "turns" } },
21+
{ "field.long": { "name": "id" } },
22+
{ "field.boolean": { "name": "success" } },
23+
{ "identity.primary": { "name": "id", "@fields": "id" } }
24+
]
25+
}
26+
},
27+
{
28+
"object.projection": {
29+
"name": "SessionSummary",
30+
"children": [
31+
{ "source.rdb": { "@kind": "view", "@view": "v_session_summary" } },
32+
{ "field.long": { "name": "id", "extends": "acme::sessions::Session.id" } },
33+
{
34+
"field.string": {
35+
"name": "allSucceeded",
36+
"children": [
37+
{
38+
"origin.aggregate": {
39+
"@agg": "all",
40+
"@via": "acme::sessions::Session.turns",
41+
"@filter": { "success": true }
42+
}
43+
}
44+
]
45+
}
46+
},
47+
{ "identity.primary": { "name": "id", "extends": "acme::sessions::Session.id" } }
48+
]
49+
}
50+
}
51+
]
52+
}
53+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types","metaobjects-db"]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"errors": [
3+
{
4+
"code": "ERR_INVALID_ORIGIN",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.sessions.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[2]['object.projection'].children[2]['field.boolean'].children[0]['origin.aggregate']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::sessions",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Session",
8+
"children": [
9+
{ "source.rdb": { "@table": "sessions" } },
10+
{ "field.long": { "name": "id" } },
11+
{ "relationship.association": { "name": "turns", "@objectRef": "acme::sessions::Turn", "@cardinality": "many" } },
12+
{ "identity.primary": { "name": "id", "@fields": "id" } }
13+
]
14+
}
15+
},
16+
{
17+
"object.entity": {
18+
"name": "Turn",
19+
"children": [
20+
{ "source.rdb": { "@table": "turns" } },
21+
{ "field.long": { "name": "id" } },
22+
{ "field.boolean": { "name": "success" } },
23+
{ "identity.primary": { "name": "id", "@fields": "id" } }
24+
]
25+
}
26+
},
27+
{
28+
"object.projection": {
29+
"name": "SessionSummary",
30+
"children": [
31+
{ "source.rdb": { "@kind": "view", "@view": "v_session_summary" } },
32+
{ "field.long": { "name": "id", "extends": "acme::sessions::Session.id" } },
33+
{
34+
"field.boolean": {
35+
"name": "allSucceeded",
36+
"children": [
37+
{
38+
"origin.aggregate": {
39+
"@agg": "all",
40+
"@via": "acme::sessions::Session.turns"
41+
}
42+
}
43+
]
44+
}
45+
},
46+
{ "identity.primary": { "name": "id", "extends": "acme::sessions::Session.id" } }
47+
]
48+
}
49+
}
50+
]
51+
}
52+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types","metaobjects-db"]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"errors": [
3+
{
4+
"code": "ERR_INVALID_ORIGIN",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.commerce.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[2]['object.projection'].children[2]['field.long'].children[0]['origin.aggregate']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme::commerce",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{ "source.rdb": { "@table": "orders" } },
10+
{ "field.long": { "name": "id" } },
11+
{ "relationship.association": { "name": "items", "@objectRef": "acme::commerce::Item", "@cardinality": "many" } },
12+
{ "identity.primary": { "name": "id", "@fields": "id" } }
13+
]
14+
}
15+
},
16+
{
17+
"object.entity": {
18+
"name": "Item",
19+
"children": [
20+
{ "source.rdb": { "@table": "items" } },
21+
{ "field.long": { "name": "id" } },
22+
{ "field.string": { "name": "category" } },
23+
{ "identity.primary": { "name": "id", "@fields": "id" } }
24+
]
25+
}
26+
},
27+
{
28+
"object.projection": {
29+
"name": "OrderSummary",
30+
"children": [
31+
{ "source.rdb": { "@kind": "view", "@view": "v_order_summary" } },
32+
{ "field.long": { "name": "id", "extends": "acme::commerce::Order.id" } },
33+
{
34+
"field.long": {
35+
"name": "categories",
36+
"isArray": true,
37+
"children": [
38+
{
39+
"origin.aggregate": {
40+
"@agg": "collect",
41+
"@of": "acme::commerce::Item.category",
42+
"@via": "acme::commerce::Order.items"
43+
}
44+
}
45+
]
46+
}
47+
},
48+
{ "identity.primary": { "name": "id", "extends": "acme::commerce::Order.id" } }
49+
]
50+
}
51+
}
52+
]
53+
}
54+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
["metaobjects-core-types","metaobjects-db"]

0 commit comments

Comments
 (0)