Files
cleveragents-core/features/steps/uko_l2_vocabulary_steps.py
T
hamza.khyari 3c014a9565
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 23s
CI / typecheck (pull_request) Successful in 32s
CI / security (pull_request) Successful in 43s
CI / unit_tests (pull_request) Successful in 2m2s
CI / integration_tests (pull_request) Successful in 2m41s
CI / docker (pull_request) Successful in 49s
CI / coverage (pull_request) Successful in 5m32s
CI / benchmark-regression (pull_request) Successful in 36m2s
feat(acms): implement UKO Layer 2 Paradigm Vocabularies (uko-oo, uko-func, uko-proc)
2026-03-13 18:21:50 +00:00

428 lines
15 KiB
Python

"""Behave steps for UKO Layer 2 vocabulary model and registry tests.
All step definitions use the ``for uko_l2`` suffix to avoid
AmbiguousStep collisions with other feature files.
"""
from __future__ import annotations
from _uko_l2_test_helpers import capture_error
from behave import given, then, when
from behave.runner import Context
from pydantic import ValidationError
from cleveragents.acms.uko.vocabularies import (
VocabularyClass,
VocabularyProperty,
get_func_vocabulary,
get_oo_vocabulary,
get_proc_vocabulary,
)
# ---------------------------------------------------------------------------
# VocabularyClass -- construction
# ---------------------------------------------------------------------------
@given('a simple vocabulary class with uri "{uri}" and label "{label}" for uko_l2')
def step_create_vocab_class(ctx: Context, uri: str, label: str) -> None:
ctx.vocab_class = VocabularyClass(uri=uri, label=label)
@given(
'a vocabulary class with uri "{uri}" and label "{label}" '
'and parents "{parents}" for uko_l2'
)
def step_create_vocab_class_parents(
ctx: Context, uri: str, label: str, parents: str
) -> None:
parent_uris = tuple(p.strip() for p in parents.split(","))
ctx.vocab_class = VocabularyClass(uri=uri, label=label, parent_uris=parent_uris)
@then('the vocabulary class uri should be "{expected}" for uko_l2')
def step_check_vocab_class_uri(ctx: Context, expected: str) -> None:
assert ctx.vocab_class.uri == expected
@then('the vocabulary class label should be "{expected}" for uko_l2')
def step_check_vocab_class_label(ctx: Context, expected: str) -> None:
assert ctx.vocab_class.label == expected
@then("the vocabulary class comment should be empty for uko_l2")
def step_check_vocab_class_comment_empty(ctx: Context) -> None:
assert ctx.vocab_class.comment == ""
@then("the vocabulary class parent_uris should be empty for uko_l2")
def step_check_vocab_class_parents_empty(ctx: Context) -> None:
assert ctx.vocab_class.parent_uris == ()
@then("the vocabulary class should have {count:d} parent URIs for uko_l2")
def step_check_vocab_class_parent_count(ctx: Context, count: int) -> None:
assert len(ctx.vocab_class.parent_uris) == count
@then('the vocabulary class parent {index:d} should be "{expected}" for uko_l2')
def step_check_vocab_class_parent_idx(ctx: Context, index: int, expected: str) -> None:
assert ctx.vocab_class.parent_uris[index] == expected
# ---------------------------------------------------------------------------
# VocabularyClass -- validation
# ---------------------------------------------------------------------------
@when("I create a vocabulary class with empty uri for uko_l2")
def step_create_vocab_class_empty_uri(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyClass(uri="", label="Test")
@when("I create a vocabulary class with whitespace uri for uko_l2")
def step_create_vocab_class_whitespace_uri(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyClass(uri=" ", label="Test")
@when("I create a vocabulary class with empty label for uko_l2")
def step_create_vocab_class_empty_label(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyClass(uri="http://example.org/test", label="")
@then("a validation error should be raised for uko_l2")
def step_check_validation_error(ctx: Context) -> None:
assert ctx.error is not None, "Expected a validation error but none was raised"
assert isinstance(ctx.error, (ValidationError, ValueError))
# ---------------------------------------------------------------------------
# VocabularyClass -- frozen
# ---------------------------------------------------------------------------
@when("I try to mutate the vocabulary class uri for uko_l2")
def step_mutate_vocab_class(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
ctx.vocab_class.uri = "http://changed" # type: ignore[misc] # deliberate mutation to test frozen model
@then("a frozen mutation error should be raised for uko_l2")
def step_check_frozen_error(ctx: Context) -> None:
assert ctx.error is not None, "Expected a frozen mutation error"
# ---------------------------------------------------------------------------
# VocabularyProperty -- construction
# ---------------------------------------------------------------------------
@given(
'a basic vocabulary property with uri "{uri}" and label "{label}" '
'and domain "{domain}" and range "{range_uri}" for uko_l2'
)
def step_create_vocab_property(
ctx: Context, uri: str, label: str, domain: str, range_uri: str
) -> None:
ctx.vocab_property = VocabularyProperty(
uri=uri, label=label, domain_uri=domain, range_uri=range_uri
)
@given(
'a vocabulary property with uri "{uri}" and label "{label}" '
'and domain "{domain}" and range "{range_uri}" '
'and sub_property_of "{sub_prop}" for uko_l2'
)
def step_create_vocab_property_sub(
ctx: Context,
uri: str,
label: str,
domain: str,
range_uri: str,
sub_prop: str,
) -> None:
ctx.vocab_property = VocabularyProperty(
uri=uri,
label=label,
domain_uri=domain,
range_uri=range_uri,
sub_property_of=sub_prop,
)
@then('the vocabulary property uri should be "{expected}" for uko_l2')
def step_check_vocab_prop_uri(ctx: Context, expected: str) -> None:
assert ctx.vocab_property.uri == expected
@then('the vocabulary property label should be "{expected}" for uko_l2')
def step_check_vocab_prop_label(ctx: Context, expected: str) -> None:
assert ctx.vocab_property.label == expected
@then("the vocabulary property sub_property_of should be empty for uko_l2")
def step_check_vocab_prop_sub_empty(ctx: Context) -> None:
assert ctx.vocab_property.sub_property_of is None
@then('the vocabulary property sub_property_of should be "{expected}" for uko_l2')
def step_check_vocab_prop_sub(ctx: Context, expected: str) -> None:
assert ctx.vocab_property.sub_property_of == expected
@when("I create a vocabulary property with empty uri for uko_l2")
def step_create_vocab_prop_empty_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",
)
@when("I try to mutate the vocabulary property uri for uko_l2")
def step_mutate_vocab_property(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
ctx.vocab_property.uri = "http://changed" # type: ignore[misc] # deliberate mutation to test frozen model
# ---------------------------------------------------------------------------
# Paradigm vocabularies -- OO, Func, Proc
# ---------------------------------------------------------------------------
@given("the uko-oo vocabulary for uko_l2")
def step_get_oo_vocab(ctx: Context) -> None:
ctx.vocabulary = get_oo_vocabulary()
@given("the uko-func vocabulary for uko_l2")
def step_get_func_vocab(ctx: Context) -> None:
ctx.vocabulary = get_func_vocabulary()
@given("the uko-proc vocabulary for uko_l2")
def step_get_proc_vocab(ctx: Context) -> None:
ctx.vocabulary = get_proc_vocabulary()
@then('the vocabulary should have prefix "{expected}" for uko_l2')
def step_check_vocab_prefix(ctx: Context, expected: str) -> None:
assert ctx.vocabulary.prefix == expected
@then('the vocabulary should have IRI "{expected}" for uko_l2')
def step_check_vocab_iri(ctx: Context, expected: str) -> None:
assert ctx.vocabulary.iri == expected
@then("the vocabulary should have layer {layer:d} for uko_l2")
def step_check_vocab_layer(ctx: Context, layer: int) -> None:
assert ctx.vocabulary.layer == layer
@then("the vocabulary should have {count:d} classes for uko_l2")
def step_check_vocab_class_count(ctx: Context, count: int) -> None:
assert len(ctx.vocabulary.classes) == count
@then("the vocabulary should have {count:d} properties for uko_l2")
def step_check_vocab_prop_count(ctx: Context, count: int) -> None:
assert len(ctx.vocabulary.properties) == count
@then('the vocabulary should contain class "{name}" for uko_l2')
def step_check_vocab_has_class(ctx: Context, name: str) -> None:
found = ctx.vocabulary.get_class(name)
assert found is not None, f"Class '{name}' not found"
@then('the vocabulary should contain property "{name}" for uko_l2')
def step_check_vocab_has_property(ctx: Context, name: str) -> None:
found = ctx.vocabulary.get_property(name)
assert found is not None, f"Property '{name}' not found"
@when('I look up class "{name}" for uko_l2')
def step_lookup_class(ctx: Context, name: str) -> None:
ctx.looked_up_class = ctx.vocabulary.get_class(name)
@when('I look up property "{name}" for uko_l2')
def step_lookup_property(ctx: Context, name: str) -> None:
ctx.looked_up_property = ctx.vocabulary.get_property(name)
@then('the class should have parent "{expected}" for uko_l2')
def step_check_class_parent(ctx: Context, expected: str) -> None:
assert ctx.looked_up_class is not None, "Class was not looked up"
assert expected in ctx.looked_up_class.parent_uris, (
f"Expected parent '{expected}' not found in {ctx.looked_up_class.parent_uris}"
)
@then('the property domain should be "{expected}" for uko_l2')
def step_check_prop_domain(ctx: Context, expected: str) -> None:
assert ctx.looked_up_property is not None
assert ctx.looked_up_property.domain_uri == expected
@then('the property range should be "{expected}" for uko_l2')
def step_check_prop_range(ctx: Context, expected: str) -> None:
assert ctx.looked_up_property is not None
assert ctx.looked_up_property.range_uri == expected
@then('the property sub_property_of should be "{expected}" for uko_l2')
def step_check_prop_sub_property(ctx: Context, expected: str) -> None:
assert ctx.looked_up_property is not None
assert ctx.looked_up_property.sub_property_of == expected
@then("the looked up class should not be None for uko_l2")
def step_class_not_none(ctx: Context) -> None:
assert ctx.looked_up_class is not None
@then("the looked up class should be None for uko_l2")
def step_class_is_none(ctx: Context) -> None:
assert ctx.looked_up_class is None
@then('the looked up class uri should be "{expected}" for uko_l2')
def step_check_looked_up_class_uri(ctx: Context, expected: str) -> None:
assert ctx.looked_up_class is not None
assert ctx.looked_up_class.uri == expected
@then("the looked up property should not be None for uko_l2")
def step_prop_not_none(ctx: Context) -> None:
assert ctx.looked_up_property is not None
@then("the looked up property should be None for uko_l2")
def step_prop_is_none(ctx: Context) -> None:
assert ctx.looked_up_property is None
@then("class_uris should have {count:d} entries for uko_l2")
def step_check_class_uris_count(ctx: Context, count: int) -> None:
assert len(ctx.vocabulary.class_uris()) == count
@then("property_uris should have {count:d} entries for uko_l2")
def step_check_property_uris_count(ctx: Context, count: int) -> None:
assert len(ctx.vocabulary.property_uris()) == count
# ---------------------------------------------------------------------------
# Review findings: M8 — empty label/domain/range rejection
# ---------------------------------------------------------------------------
@when("I create a vocabulary property with empty label for uko_l2")
def step_create_vocab_prop_empty_label(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="",
domain_uri="https://example.com/d",
range_uri="https://example.com/r",
)
@when("I create a vocabulary property with empty domain for uko_l2")
def step_create_vocab_prop_empty_domain(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="test",
domain_uri="",
range_uri="https://example.com/r",
)
@when("I create a vocabulary property with empty range for uko_l2")
def step_create_vocab_prop_empty_range(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="test",
domain_uri="https://example.com/d",
range_uri="",
)
# ---------------------------------------------------------------------------
# Review findings: M9 — invalid URI scheme rejection
# ---------------------------------------------------------------------------
@when("I create a vocabulary class with non-http uri for uko_l2")
def step_create_vocab_class_non_http(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyClass(
uri="ftp://example.com/BadClass",
label="BadClass",
)
@when("I create a vocabulary property with non-http uri for uko_l2")
def step_create_vocab_prop_non_http(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="urn:example:bad",
label="bad",
domain_uri="https://example.com/d",
range_uri="https://example.com/r",
)
@when("I create a vocabulary class with non-http parent uri for uko_l2")
def step_create_vocab_class_non_http_parent(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyClass(
uri="https://example.com/Good",
label="Good",
parent_uris=("ftp://example.com/BadParent",),
)
@when("I create a vocabulary property with non-http domain uri for uko_l2")
def step_create_vocab_prop_non_http_domain(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="prop",
domain_uri="ftp://example.com/BadDomain",
range_uri="https://example.com/r",
)
@when("I create a vocabulary property with non-http range uri for uko_l2")
def step_create_vocab_prop_non_http_range(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="prop",
domain_uri="https://example.com/d",
range_uri="file:///etc/passwd",
)
@when("I create a vocabulary property with non-http sub_property_of for uko_l2")
def step_create_vocab_prop_non_http_subprop(ctx: Context) -> None:
with capture_error(ctx, ValidationError):
VocabularyProperty(
uri="https://example.com/prop",
label="prop",
domain_uri="https://example.com/d",
range_uri="https://example.com/r",
sub_property_of="javascript:alert(1)",
)