Skip to content

Commit 3f8c222

Browse files
dmealingclaude
andcommitted
test(conformance): FR-011 enum-coercion attr-validation negative fixtures (4 loader ports)
Adds three shared metamodel-conformance negative fixtures covering FR-011 field.enum coercion-attribute validation: - error-enum-coerce-default-non-member (@coerceDefault not in @values) - error-enum-default-non-member (@default not in @values) - error-enum-normalize-bad-mode (@normalize not in none|collapse|strip) Each expects a single ERR_BAD_ATTR_VALUE on the field.enum node. TS, C#, and Java loaders pass all three unchanged. Python loader fix: the FR-011 validation pass re-validated @normalize against NORMALIZE_MODES even though the field.enum attr schema already gates it via allowed_values, double-reporting ERR_BAD_ATTR_VALUE on the same node. Removed the redundant check (and its now-unused imports) so the envelope carries one entry, matching the cross-port single-error contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 55e3cb1 commit 3f8c222

7 files changed

Lines changed: 100 additions & 15 deletions

File tree

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_BAD_ATTR_VALUE",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.enums.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[0]['object.entity'].children[1]['field.enum']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{ "field.long": { "name": "id" } },
10+
{ "field.enum": { "name": "status", "@values": ["NEW", "DONE"], "@coerceDefault": "BOGUS" } },
11+
{ "identity.primary": { "@fields": "id" } }
12+
]
13+
}
14+
}
15+
]
16+
}
17+
}
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_BAD_ATTR_VALUE",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.enums.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[0]['object.entity'].children[1]['field.enum']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{ "field.long": { "name": "id" } },
10+
{ "field.enum": { "name": "status", "@values": ["NEW", "DONE"], "@default": "BOGUS" } },
11+
{ "identity.primary": { "@fields": "id" } }
12+
]
13+
}
14+
}
15+
]
16+
}
17+
}
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_BAD_ATTR_VALUE",
5+
"source": {
6+
"format": "json",
7+
"files": [
8+
"meta.enums.json"
9+
],
10+
"jsonPath": "$['metadata.root'].children[0]['object.entity'].children[1]['field.enum']"
11+
}
12+
}
13+
],
14+
"warnings": []
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"metadata.root": {
3+
"package": "acme",
4+
"children": [
5+
{
6+
"object.entity": {
7+
"name": "Order",
8+
"children": [
9+
{ "field.long": { "name": "id" } },
10+
{ "field.enum": { "name": "status", "@values": ["NEW", "DONE"], "@normalize": "fuzzy" } },
11+
{ "identity.primary": { "@fields": "id" } }
12+
]
13+
}
14+
}
15+
]
16+
}
17+
}

server/python/src/metaobjects/loader/validation_passes.py

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@
1414
ENUM_MEMBER_PATTERN,
1515
FIELD_ATTR_COERCE_DEFAULT,
1616
FIELD_ATTR_DEFAULT,
17-
FIELD_ATTR_NORMALIZE,
1817
FIELD_ATTR_OBJECT_REF,
1918
FIELD_ATTR_STORAGE,
2019
FIELD_ATTR_VALUES,
2120
FIELD_SUBTYPE_ENUM,
2221
FIELD_SUBTYPE_OBJECT,
23-
NORMALIZE_MODES,
2422
)
2523
from ..meta.core.object.meta_object import MetaObject
2624
from ..meta.meta_data import MetaData
@@ -343,8 +341,10 @@ def _validate_enum_fr011_attrs(node: MetaData, errors: list[MetaError]) -> None:
343341
(own or inherited) → ``ERR_BAD_ATTR_VALUE``.
344342
* ``@default`` (own, the absent-fill member) must likewise be a member of the
345343
effective ``@values`` → ``ERR_BAD_ATTR_VALUE``.
346-
* ``@normalize`` (own) must be one of ``none|collapse|strip``
347-
→ ``ERR_BAD_ATTR_VALUE`` (belt-and-braces with the registered allowed_values).
344+
``@normalize`` mode validation is NOT done here: it is a closed enum gated by the
345+
registered ``allowed_values=NORMALIZE_MODES`` on the ``field.enum`` attr schema, so the
346+
generic attr-schema pass already emits the single ``ERR_BAD_ATTR_VALUE``. Re-checking it
347+
here double-reported the same node (one envelope entry per port is the cross-port contract).
348348
349349
Own-only policy: only checks attrs declared on THIS node, matching the ``@values``
350350
pass. The membership set is read effectively so an enum that owns ``@coerceDefault``
@@ -369,17 +369,6 @@ def _validate_enum_fr011_attrs(node: MetaData, errors: list[MetaError]) -> None:
369369
)
370370
)
371371

372-
own_normalize = node.attr(FIELD_ATTR_NORMALIZE)
373-
if isinstance(own_normalize, str) and own_normalize not in NORMALIZE_MODES:
374-
errors.append(
375-
MetaError(
376-
f"{label} attribute '@{FIELD_ATTR_NORMALIZE}' value {own_normalize!r} "
377-
f"is not a valid mode; allowed: {', '.join(NORMALIZE_MODES)}",
378-
ErrorCode.ERR_BAD_ATTR_VALUE,
379-
envelope=node.source,
380-
)
381-
)
382-
383372

384373
# ---------------------------------------------------------------------------
385374
# Pass: dataGrid @defaultSortField validation

0 commit comments

Comments
 (0)