"""Behave steps for UKO Layer 2 TTL validation and additional coverage. Covers Turtle file loading, ParadigmVocabulary frozen-model checks, and miscellaneous validation edge-cases. All step definitions use the ``for uko_l2`` suffix to avoid AmbiguousStep collisions with other feature files. """ from __future__ import annotations from pathlib import Path from _uko_l2_test_helpers import capture_error from behave import then, when from behave.runner import Context from pydantic import ValidationError from cleveragents.acms.uko.vocabularies import ( ParadigmVocabulary, VocabularyProperty, get_oo_vocabulary, ) from cleveragents.application.services.uko_loader import UKOLoader _TTL_PATH = Path(__file__).resolve().parents[2] / "docs" / "ontology" / "uko.ttl" # --------------------------------------------------------------------------- # Turtle validation # --------------------------------------------------------------------------- @when("I load the UKO TTL file for uko_l2") def step_load_ttl(ctx: Context) -> None: loader = UKOLoader() ctx.ontology = loader.load(_TTL_PATH) @then("the ontology should load without errors for uko_l2") def step_check_ontology_loaded(ctx: Context) -> None: assert ctx.ontology is not None assert len(ctx.ontology.nodes) > 0 @then("the ontology should contain uko-func prefix for uko_l2") def step_check_func_prefix(ctx: Context) -> None: prefixes = {p.prefix for p in ctx.ontology.prefixes} assert "uko-func" in prefixes, f"uko-func not in {prefixes}" @then("the ontology should contain uko-proc prefix for uko_l2") def step_check_proc_prefix(ctx: Context) -> None: prefixes = {p.prefix for p in ctx.ontology.prefixes} assert "uko-proc" in prefixes, f"uko-proc not in {prefixes}" @then('the ontology should contain node "{label}" for uko_l2') def step_check_node_exists(ctx: Context, label: str) -> None: labels = {n.label for n in ctx.ontology.nodes} assert label in labels, f"Node '{label}' not in {labels}" @then("the ontology should have at least {count:d} layer 2 nodes for uko_l2") def step_check_layer2_count(ctx: Context, count: int) -> None: layer2 = [n for n in ctx.ontology.nodes if n.layer == 2] assert len(layer2) >= count, f"Expected >= {count} layer 2 nodes, got {len(layer2)}" # --------------------------------------------------------------------------- # ParadigmVocabulary frozen # --------------------------------------------------------------------------- @when("I try to mutate the vocabulary prefix for uko_l2") def step_mutate_vocab_prefix(ctx: Context) -> None: with capture_error(ctx, ValidationError): ctx.vocabulary.prefix = "changed:" # type: ignore[misc] # deliberate mutation to test frozen model # --------------------------------------------------------------------------- # Additional coverage steps # --------------------------------------------------------------------------- @when("I create a paradigm vocabulary with empty prefix for uko_l2") def step_create_vocab_empty_prefix(ctx: Context) -> None: with capture_error(ctx, ValidationError): ParadigmVocabulary( prefix="", iri="http://test", layer=2, parent_domain="uko-code:", max_depth=0, ) @when("I create a paradigm vocabulary with whitespace prefix for uko_l2") def step_create_vocab_whitespace_prefix(ctx: Context) -> None: with capture_error(ctx, ValidationError): ParadigmVocabulary( prefix=" ", iri="http://test", layer=2, parent_domain="uko-code:", max_depth=0, ) @when("I create a vocabulary property with whitespace uri for uko_l2") def step_create_vocab_prop_whitespace_uri(ctx: Context) -> None: with capture_error(ctx, ValidationError): VocabularyProperty( uri=" ", label="test", domain_uri="https://example.com/d", range_uri="https://example.com/r", ) @then("the registry should return False for non-string contains for uko_l2") def step_check_non_string_contains(ctx: Context) -> None: assert 42 not in ctx.registry assert None not in ctx.registry @when("I try to register a vocabulary with duplicate IRI for uko_l2") def step_register_duplicate_iri(ctx: Context) -> None: oo = get_oo_vocabulary() dup = ParadigmVocabulary( prefix="uko-dup:", iri=oo.iri, layer=2, parent_domain="uko-code:", ) with capture_error(ctx, ValueError): ctx.registry.register(dup) @when("I create a paradigm vocabulary with prefix missing colon for uko_l2") def step_create_paradigm_vocab_no_colon(ctx: Context) -> None: with capture_error(ctx, ValidationError): ParadigmVocabulary( prefix="uko-test", iri="https://example.org/test#", layer=2, parent_domain="uko-code:", )