89eaee008d
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 19s
CI / build (pull_request) Successful in 27s
CI / quality (pull_request) Successful in 29s
CI / security (pull_request) Successful in 41s
CI / typecheck (pull_request) Successful in 45s
CI / e2e_tests (pull_request) Successful in 1m29s
CI / unit_tests (pull_request) Successful in 3m19s
CI / integration_tests (pull_request) Successful in 3m30s
CI / docker (pull_request) Successful in 54s
CI / coverage (pull_request) Successful in 6m8s
CI / benchmark-regression (pull_request) Successful in 38m9s
Implement Layer 3 technology-specific UKO vocabulary extensions for Python, TypeScript, Rust, and Java with language-specific classes, properties, and DetailLevelMap insertions. - 4 OWL/Turtle ontology files with language-specific semantic classes - DetailLevelMap insertion logic with correct integer reassignment - Provenance contract (5 required fields per spec) - Full 4-layer chain resolution (Layer 3 -> Layer 2 -> Layer 1 -> Layer 0) - Comprehensive Behave test suite (63 scenarios) ISSUES CLOSED: #576
529 lines
18 KiB
Python
529 lines
18 KiB
Python
"""Step definitions for UKO Layer 3 Technology Vocabularies -- base models and vocabs.
|
|
|
|
All steps use the suffix "for uko_l3" to avoid AmbiguousStep errors.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from typing import Any
|
|
|
|
from behave import given, then
|
|
from pydantic import ValidationError
|
|
|
|
from cleveragents.acms.uko.layer3_java import JAVA_VOCABULARY, JavaClass
|
|
from cleveragents.acms.uko.layer3_py import (
|
|
PYTHON_VOCABULARY,
|
|
PythonClass,
|
|
)
|
|
from cleveragents.acms.uko.layer3_rs import RUST_VOCABULARY, RustStruct
|
|
from cleveragents.acms.uko.layer3_ts import TYPESCRIPT_VOCABULARY, TypeScriptClass
|
|
from cleveragents.acms.uko.vocabulary import (
|
|
Layer2Dependency,
|
|
ProvenanceInfo,
|
|
UKOClass,
|
|
UKOProperty,
|
|
UKOVocabulary,
|
|
)
|
|
|
|
__all__: list[str] = []
|
|
|
|
# -- ProvenanceInfo steps --
|
|
|
|
|
|
@given('a provenance info with resource "{resource}" and path "{path}" for uko_l3')
|
|
def step_given_provenance_info(context: Any, resource: str, path: str) -> None:
|
|
context.provenance = ProvenanceInfo(
|
|
source_resource=resource,
|
|
source_path=path,
|
|
)
|
|
|
|
|
|
@given(
|
|
'a ranged provenance info with resource "{resource}" and path '
|
|
'"{path}" and range "{source_range}" for uko_l3'
|
|
)
|
|
def step_given_provenance_info_with_range(
|
|
context: Any, resource: str, path: str, source_range: str
|
|
) -> None:
|
|
context.provenance = ProvenanceInfo(
|
|
source_resource=resource,
|
|
source_path=path,
|
|
source_range=source_range,
|
|
)
|
|
|
|
|
|
@then('the provenance source_resource should be "{expected}" for uko_l3')
|
|
def step_then_provenance_resource(context: Any, expected: str) -> None:
|
|
assert context.provenance.source_resource == expected
|
|
|
|
|
|
@then('the provenance source_path should be "{expected}" for uko_l3')
|
|
def step_then_provenance_path(context: Any, expected: str) -> None:
|
|
assert context.provenance.source_path == expected
|
|
|
|
|
|
@then("the provenance is_current should be true for uko_l3")
|
|
def step_then_provenance_is_current(context: Any) -> None:
|
|
assert context.provenance.is_current is True
|
|
|
|
|
|
@then("the provenance valid_from should be a UTC datetime for uko_l3")
|
|
def step_then_provenance_valid_from_utc(context: Any) -> None:
|
|
vf = context.provenance.valid_from
|
|
assert isinstance(vf, datetime)
|
|
assert vf.tzinfo is not None
|
|
assert vf.tzinfo == UTC
|
|
|
|
|
|
@then('the provenance source_range should be "{expected}" for uko_l3')
|
|
def step_then_provenance_source_range(context: Any, expected: str) -> None:
|
|
assert context.provenance.source_range == expected
|
|
|
|
|
|
@then(
|
|
"creating a provenance info with empty source_resource "
|
|
"should raise ValueError for uko_l3"
|
|
)
|
|
def step_then_provenance_empty_resource(context: Any) -> None:
|
|
try:
|
|
ProvenanceInfo(source_resource="", source_path="some.py")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then(
|
|
"creating a provenance info with whitespace source_resource "
|
|
"should raise ValueError for uko_l3"
|
|
)
|
|
def step_then_provenance_whitespace_resource(context: Any) -> None:
|
|
try:
|
|
ProvenanceInfo(source_resource=" ", source_path="some.py")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then(
|
|
"creating a provenance info with empty source_path "
|
|
"should raise ValueError for uko_l3"
|
|
)
|
|
def step_then_provenance_empty_path(context: Any) -> None:
|
|
try:
|
|
ProvenanceInfo(source_resource="res-01", source_path="")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then(
|
|
"creating a provenance info with whitespace source_path "
|
|
"should raise ValueError for uko_l3"
|
|
)
|
|
def step_then_provenance_whitespace_path(context: Any) -> None:
|
|
try:
|
|
ProvenanceInfo(source_resource="res-01", source_path=" ")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then("modifying provenance is_current should raise an error for uko_l3")
|
|
def step_then_provenance_immutable(context: Any) -> None:
|
|
try:
|
|
context.provenance.is_current = False
|
|
msg = "Expected immutability error"
|
|
raise AssertionError(msg)
|
|
except (TypeError, AttributeError, ValidationError):
|
|
pass
|
|
|
|
|
|
# -- UKOClass steps --
|
|
|
|
|
|
@given('a UKO class with uri "{uri}" and label "{label}" for uko_l3')
|
|
def step_given_uko_class(context: Any, uri: str, label: str) -> None:
|
|
context.uko_class = UKOClass(uri=uri, label=label)
|
|
|
|
|
|
@then('the UKO class uri should be "{expected}" for uko_l3')
|
|
def step_then_uko_class_uri(context: Any, expected: str) -> None:
|
|
assert context.uko_class.uri == expected
|
|
|
|
|
|
@then('the UKO class label should be "{expected}" for uko_l3')
|
|
def step_then_uko_class_label(context: Any, expected: str) -> None:
|
|
assert context.uko_class.label == expected
|
|
|
|
|
|
@then("the UKO class subclass_of should be empty for uko_l3")
|
|
def step_then_uko_class_subclass_empty(context: Any) -> None:
|
|
assert context.uko_class.subclass_of == ()
|
|
|
|
|
|
@then("creating a UKO class with empty uri should raise ValueError for uko_l3")
|
|
def step_then_uko_class_empty_uri(context: Any) -> None:
|
|
try:
|
|
UKOClass(uri="", label="X")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then("creating a UKO class with whitespace label should raise ValueError for uko_l3")
|
|
def step_then_uko_class_whitespace_label(context: Any) -> None:
|
|
try:
|
|
UKOClass(uri="https://example.com/X", label=" ")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
@then("modifying UKO class label should raise an error for uko_l3")
|
|
def step_then_uko_class_immutable(context: Any) -> None:
|
|
try:
|
|
context.uko_class.label = "mutated"
|
|
msg = "Expected immutability error"
|
|
raise AssertionError(msg)
|
|
except (TypeError, AttributeError, ValidationError):
|
|
pass
|
|
|
|
|
|
# -- UKOClass provenance steps --
|
|
|
|
|
|
@given('a UKO class with provenance resource "{resource}" and path "{path}" for uko_l3')
|
|
def step_given_uko_class_with_provenance(
|
|
context: Any, resource: str, path: str
|
|
) -> None:
|
|
context.uko_class = UKOClass(
|
|
uri="https://example.com/WithProv",
|
|
label="WithProv",
|
|
provenance=ProvenanceInfo(
|
|
source_resource=resource,
|
|
source_path=path,
|
|
),
|
|
)
|
|
|
|
|
|
@then('the UKO class provenance source_resource should be "{expected}" for uko_l3')
|
|
def step_then_uko_class_prov_resource(context: Any, expected: str) -> None:
|
|
assert context.uko_class.provenance is not None
|
|
assert context.uko_class.provenance.source_resource == expected
|
|
|
|
|
|
@then('the UKO class provenance source_path should be "{expected}" for uko_l3')
|
|
def step_then_uko_class_prov_path(context: Any, expected: str) -> None:
|
|
assert context.uko_class.provenance is not None
|
|
assert context.uko_class.provenance.source_path == expected
|
|
|
|
|
|
@then("the UKO class provenance should be None for uko_l3")
|
|
def step_then_uko_class_prov_none(context: Any) -> None:
|
|
assert context.uko_class.provenance is None
|
|
|
|
|
|
# -- UKOProperty steps --
|
|
|
|
|
|
@given('a UKO property with uri "{uri}" and label "{label}" for uko_l3')
|
|
def step_given_uko_property(context: Any, uri: str, label: str) -> None:
|
|
context.uko_property = UKOProperty(uri=uri, label=label)
|
|
|
|
|
|
@then('the UKO property uri should be "{expected}" for uko_l3')
|
|
def step_then_uko_property_uri(context: Any, expected: str) -> None:
|
|
assert context.uko_property.uri == expected
|
|
|
|
|
|
@then("the UKO property is_object_property should be true for uko_l3")
|
|
def step_then_uko_property_is_object(context: Any) -> None:
|
|
assert context.uko_property.is_object_property is True
|
|
|
|
|
|
@then("creating a UKO property with empty uri should raise ValueError for uko_l3")
|
|
def step_then_uko_property_empty_uri(context: Any) -> None:
|
|
try:
|
|
UKOProperty(uri="", label="X")
|
|
msg = "Expected ValueError"
|
|
raise AssertionError(msg)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
# -- Layer2Dependency steps --
|
|
|
|
|
|
@given('a layer2 dependency with uri "{uri}" and prefix "{prefix}" for uko_l3')
|
|
def step_given_layer2_dep(context: Any, uri: str, prefix: str) -> None:
|
|
context.layer2_dep = Layer2Dependency(uri=uri, prefix=prefix)
|
|
|
|
|
|
@then("the layer2 dependency layer should be {expected:d} for uko_l3")
|
|
def step_then_layer2_dep_layer(context: Any, expected: int) -> None:
|
|
assert context.layer2_dep.layer == expected
|
|
|
|
|
|
@then('the layer2 dependency prefix should be "{expected}" for uko_l3')
|
|
def step_then_layer2_dep_prefix(context: Any, expected: str) -> None:
|
|
assert context.layer2_dep.prefix == expected
|
|
|
|
|
|
# -- UKOVocabulary steps --
|
|
|
|
|
|
@given(
|
|
'a UKO vocabulary with namespace "{ns}" and prefix "{prefix}" '
|
|
'and label "{label}" for uko_l3'
|
|
)
|
|
def step_given_uko_vocab(context: Any, ns: str, prefix: str, label: str) -> None:
|
|
context.uko_vocab = UKOVocabulary(namespace=ns, prefix=prefix, label=label)
|
|
|
|
|
|
@then("the vocabulary layer should be {expected:d} for uko_l3")
|
|
def step_then_vocab_layer(context: Any, expected: int) -> None:
|
|
assert context.uko_vocab.layer == expected
|
|
|
|
|
|
@then('the vocabulary prefix should be "{expected}" for uko_l3')
|
|
def step_then_vocab_prefix(context: Any, expected: str) -> None:
|
|
assert context.uko_vocab.prefix == expected
|
|
|
|
|
|
# -- Python vocabulary steps --
|
|
|
|
|
|
@then('the Python vocabulary namespace should be "{expected}" for uko_l3')
|
|
def step_then_py_namespace(context: Any, expected: str) -> None:
|
|
assert PYTHON_VOCABULARY.namespace == expected
|
|
|
|
|
|
@then('the Python vocabulary prefix should be "{expected}" for uko_l3')
|
|
def step_then_py_prefix(context: Any, expected: str) -> None:
|
|
assert PYTHON_VOCABULARY.prefix == expected
|
|
|
|
|
|
@then("the Python vocabulary layer should be {expected:d} for uko_l3")
|
|
def step_then_py_layer(context: Any, expected: int) -> None:
|
|
assert PYTHON_VOCABULARY.layer == expected
|
|
|
|
|
|
@then("the Python vocabulary should have {count:d} classes for uko_l3")
|
|
def step_then_py_class_count(context: Any, count: int) -> None:
|
|
assert len(PYTHON_VOCABULARY.classes) == count
|
|
|
|
|
|
@then('the Python vocabulary should contain class "{name}" for uko_l3')
|
|
def step_then_py_has_class(context: Any, name: str) -> None:
|
|
labels = tuple(c.label for c in PYTHON_VOCABULARY.classes)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then("the Python vocabulary should have {count:d} properties for uko_l3")
|
|
def step_then_py_property_count(context: Any, count: int) -> None:
|
|
assert len(PYTHON_VOCABULARY.properties) == count
|
|
|
|
|
|
@then('the Python vocabulary should contain property "{name}" for uko_l3')
|
|
def step_then_py_has_property(context: Any, name: str) -> None:
|
|
labels = tuple(p.label for p in PYTHON_VOCABULARY.properties)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then('the Python vocabulary parent_prefixes should contain "{prefix}" for uko_l3')
|
|
def step_then_py_parent_prefix(context: Any, prefix: str) -> None:
|
|
assert prefix in PYTHON_VOCABULARY.parent_prefixes
|
|
|
|
|
|
@then('the PythonClass subclass_of should contain "{uri}" for uko_l3')
|
|
def step_then_python_class_subclass(context: Any, uri: str) -> None:
|
|
assert uri in PythonClass.subclass_of, f"{uri!r} not in {PythonClass.subclass_of}"
|
|
|
|
|
|
@then("the Python vocabulary should have {count:d} inserted levels for uko_l3")
|
|
def step_then_py_inserted_count(context: Any, count: int) -> None:
|
|
assert len(PYTHON_VOCABULARY.inserted_levels) == count
|
|
|
|
|
|
@then('the Python vocabulary inserted levels should include "{name}" for uko_l3')
|
|
def step_then_py_inserted_name(context: Any, name: str) -> None:
|
|
names = tuple(n for n, _ in PYTHON_VOCABULARY.inserted_levels)
|
|
assert name in names, f"{name!r} not in {names}"
|
|
|
|
|
|
# -- TypeScript vocabulary steps --
|
|
|
|
|
|
@then('the TypeScript vocabulary namespace should be "{expected}" for uko_l3')
|
|
def step_then_ts_namespace(context: Any, expected: str) -> None:
|
|
assert TYPESCRIPT_VOCABULARY.namespace == expected
|
|
|
|
|
|
@then('the TypeScript vocabulary prefix should be "{expected}" for uko_l3')
|
|
def step_then_ts_prefix(context: Any, expected: str) -> None:
|
|
assert TYPESCRIPT_VOCABULARY.prefix == expected
|
|
|
|
|
|
@then("the TypeScript vocabulary layer should be {expected:d} for uko_l3")
|
|
def step_then_ts_layer(context: Any, expected: int) -> None:
|
|
assert TYPESCRIPT_VOCABULARY.layer == expected
|
|
|
|
|
|
@then("the TypeScript vocabulary should have {count:d} classes for uko_l3")
|
|
def step_then_ts_class_count(context: Any, count: int) -> None:
|
|
assert len(TYPESCRIPT_VOCABULARY.classes) == count
|
|
|
|
|
|
@then('the TypeScript vocabulary should contain class "{name}" for uko_l3')
|
|
def step_then_ts_has_class(context: Any, name: str) -> None:
|
|
labels = tuple(c.label for c in TYPESCRIPT_VOCABULARY.classes)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then("the TypeScript vocabulary should have {count:d} properties for uko_l3")
|
|
def step_then_ts_property_count(context: Any, count: int) -> None:
|
|
assert len(TYPESCRIPT_VOCABULARY.properties) == count
|
|
|
|
|
|
@then('the TypeScript vocabulary should contain property "{name}" for uko_l3')
|
|
def step_then_ts_has_property(context: Any, name: str) -> None:
|
|
labels = tuple(p.label for p in TYPESCRIPT_VOCABULARY.properties)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then('the TypeScript vocabulary parent_prefixes should contain "{prefix}" for uko_l3')
|
|
def step_then_ts_parent_prefix(context: Any, prefix: str) -> None:
|
|
assert prefix in TYPESCRIPT_VOCABULARY.parent_prefixes
|
|
|
|
|
|
@then("the TypeScript vocabulary should have {count:d} inserted levels for uko_l3")
|
|
def step_then_ts_inserted_count(context: Any, count: int) -> None:
|
|
assert len(TYPESCRIPT_VOCABULARY.inserted_levels) == count
|
|
|
|
|
|
# -- Rust vocabulary steps --
|
|
|
|
|
|
@then('the Rust vocabulary namespace should be "{expected}" for uko_l3')
|
|
def step_then_rs_namespace(context: Any, expected: str) -> None:
|
|
assert RUST_VOCABULARY.namespace == expected
|
|
|
|
|
|
@then('the Rust vocabulary prefix should be "{expected}" for uko_l3')
|
|
def step_then_rs_prefix(context: Any, expected: str) -> None:
|
|
assert RUST_VOCABULARY.prefix == expected
|
|
|
|
|
|
@then("the Rust vocabulary layer should be {expected:d} for uko_l3")
|
|
def step_then_rs_layer(context: Any, expected: int) -> None:
|
|
assert RUST_VOCABULARY.layer == expected
|
|
|
|
|
|
@then("the Rust vocabulary should have {count:d} classes for uko_l3")
|
|
def step_then_rs_class_count(context: Any, count: int) -> None:
|
|
assert len(RUST_VOCABULARY.classes) == count
|
|
|
|
|
|
@then('the Rust vocabulary should contain class "{name}" for uko_l3')
|
|
def step_then_rs_has_class(context: Any, name: str) -> None:
|
|
labels = tuple(c.label for c in RUST_VOCABULARY.classes)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then("the Rust vocabulary should have {count:d} properties for uko_l3")
|
|
def step_then_rs_property_count(context: Any, count: int) -> None:
|
|
assert len(RUST_VOCABULARY.properties) == count
|
|
|
|
|
|
@then('the Rust vocabulary should contain property "{name}" for uko_l3')
|
|
def step_then_rs_has_property(context: Any, name: str) -> None:
|
|
labels = tuple(p.label for p in RUST_VOCABULARY.properties)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then('the Rust vocabulary parent_prefixes should contain "{prefix}" for uko_l3')
|
|
def step_then_rs_parent_prefix(context: Any, prefix: str) -> None:
|
|
assert prefix in RUST_VOCABULARY.parent_prefixes
|
|
|
|
|
|
@then("the Rust vocabulary should have {count:d} inserted levels for uko_l3")
|
|
def step_then_rs_inserted_count(context: Any, count: int) -> None:
|
|
assert len(RUST_VOCABULARY.inserted_levels) == count
|
|
|
|
|
|
@then('the RustStruct subclass_of should contain "{uri}" for uko_l3')
|
|
def step_then_rs_struct_subclass(context: Any, uri: str) -> None:
|
|
assert uri in RustStruct.subclass_of, f"{uri!r} not in {RustStruct.subclass_of}"
|
|
|
|
|
|
# -- Java vocabulary steps --
|
|
|
|
|
|
@then('the Java vocabulary namespace should be "{expected}" for uko_l3')
|
|
def step_then_java_namespace(context: Any, expected: str) -> None:
|
|
assert JAVA_VOCABULARY.namespace == expected
|
|
|
|
|
|
@then('the Java vocabulary prefix should be "{expected}" for uko_l3')
|
|
def step_then_java_prefix(context: Any, expected: str) -> None:
|
|
assert JAVA_VOCABULARY.prefix == expected
|
|
|
|
|
|
@then("the Java vocabulary layer should be {expected:d} for uko_l3")
|
|
def step_then_java_layer(context: Any, expected: int) -> None:
|
|
assert JAVA_VOCABULARY.layer == expected
|
|
|
|
|
|
@then("the Java vocabulary should have {count:d} classes for uko_l3")
|
|
def step_then_java_class_count(context: Any, count: int) -> None:
|
|
assert len(JAVA_VOCABULARY.classes) == count
|
|
|
|
|
|
@then('the Java vocabulary should contain class "{name}" for uko_l3')
|
|
def step_then_java_has_class(context: Any, name: str) -> None:
|
|
labels = tuple(c.label for c in JAVA_VOCABULARY.classes)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then("the Java vocabulary should have {count:d} properties for uko_l3")
|
|
def step_then_java_property_count(context: Any, count: int) -> None:
|
|
assert len(JAVA_VOCABULARY.properties) == count
|
|
|
|
|
|
@then('the Java vocabulary should contain property "{name}" for uko_l3')
|
|
def step_then_java_has_property(context: Any, name: str) -> None:
|
|
labels = tuple(p.label for p in JAVA_VOCABULARY.properties)
|
|
assert name in labels, f"{name!r} not in {labels}"
|
|
|
|
|
|
@then('the Java vocabulary parent_prefixes should contain "{prefix}" for uko_l3')
|
|
def step_then_java_parent_prefix(context: Any, prefix: str) -> None:
|
|
assert prefix in JAVA_VOCABULARY.parent_prefixes
|
|
|
|
|
|
@then("the Java vocabulary should have {count:d} inserted levels for uko_l3")
|
|
def step_then_java_inserted_count(context: Any, count: int) -> None:
|
|
assert len(JAVA_VOCABULARY.inserted_levels) == count
|
|
|
|
|
|
@then('the JavaClass subclass_of should contain "{uri}" for uko_l3')
|
|
def step_then_java_class_subclass(context: Any, uri: str) -> None:
|
|
assert uri in JavaClass.subclass_of, f"{uri!r} not in {JavaClass.subclass_of}"
|
|
|
|
|
|
# -- TypeScript subclass_of step --
|
|
|
|
|
|
@then('the TypeScriptClass subclass_of should contain "{uri}" for uko_l3')
|
|
def step_then_ts_class_subclass(context: Any, uri: str) -> None:
|
|
assert uri in TypeScriptClass.subclass_of, (
|
|
f"{uri!r} not in {TypeScriptClass.subclass_of}"
|
|
)
|