diff --git a/src/oold/validation/frame.py b/src/oold/validation/frame.py index 75a9e52..2e24092 100644 --- a/src/oold/validation/frame.py +++ b/src/oold/validation/frame.py @@ -76,11 +76,17 @@ def embedded_properties(schema: dict[str, Any]) -> list[str]: Shape is the primary signal; a scoped context is a strong hint but not mandatory, since an embed can also be mapped by the ambient top-level context. + + A scoped term only counts where the schema declares a property of that name. A schema must + reflect every ``$ref`` in its ``@context`` (``OOLD-CMP-b926``), including the ones reached + from ``$defs``, so the context carries terms for properties this schema's instances never + hold; treating those as embeds puts a property into the derived frame that no instance can + match, and framing then returns nothing. """ properties = collect_composed_properties(schema) structural = [name for name, prop in properties.items() if is_embed(prop)] terms = context_terms(schema.get("@context")) - scoped = [term for term, definition in terms.items() if "@context" in definition] + scoped = [term for term, definition in terms.items() if "@context" in definition and term in properties] # dict.fromkeys dedupes while preserving order, matching the JS Set spread. return list(dict.fromkeys([*structural, *scoped])) diff --git a/tests/test_validation/test_jsonld.py b/tests/test_validation/test_jsonld.py index 5ff16a7..a280294 100644 --- a/tests/test_validation/test_jsonld.py +++ b/tests/test_validation/test_jsonld.py @@ -122,6 +122,28 @@ def test_embedded_properties_follows_allof_composition(): assert embedded_properties(schema) == ["inherited"] +def test_embedded_properties_ignores_a_scoped_term_that_is_not_a_property_here(): + """A $defs subschema's $ref is reflected in the root @context (OOLD-CMP-b926). + + Its term carries a scoped @context, but the root object has no such property, so an instance + can never hold one. Counting it would put an unmatchable property into the derived frame. + """ + schema = { + "properties": {"type": {"type": "array", "items": {"type": "string"}}}, + "$defs": {"Component": {"properties": {"amount": {"$ref": "https://oo-ld.test/x/MassFraction.schema.json"}}}}, + "@context": { + "type": {"@id": "@type", "@container": "@set"}, + "amount": { + "@id": "https://example.org/amount", + "@type": "@id", + "@context": "https://oo-ld.test/x/MassFraction.schema.json", + }, + }, + } + assert embedded_properties(schema) == [] + assert "amount" not in schema_to_frame(schema, "https://oo-ld.test/x/C.schema.json") + + # ------------------------------------------------------------------ loader