forked from HAL9000/cleveragents-core
8ea00f5185
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me> Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
252 lines
8.9 KiB
Python
252 lines
8.9 KiB
Python
"""Behave step definitions for domain repository protocol compliance tests.
|
|
|
|
Verifies that:
|
|
1. Protocol interfaces are defined in the domain layer (not infrastructure)
|
|
2. Infrastructure repositories satisfy the domain protocols
|
|
3. All protocols are runtime-checkable
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Background
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("the domain repository protocols are importable")
|
|
def step_protocols_importable(context: Any) -> None:
|
|
"""Verify all domain repository protocols can be imported."""
|
|
from cleveragents.domain.repositories import (
|
|
ActionRepositoryProtocol,
|
|
DecisionRepositoryProtocol,
|
|
LifecyclePlanRepositoryProtocol,
|
|
ProjectRepositoryProtocol,
|
|
)
|
|
|
|
context.protocols = {
|
|
"LifecyclePlanRepositoryProtocol": LifecyclePlanRepositoryProtocol,
|
|
"ActionRepositoryProtocol": ActionRepositoryProtocol,
|
|
"DecisionRepositoryProtocol": DecisionRepositoryProtocol,
|
|
"ProjectRepositoryProtocol": ProjectRepositoryProtocol,
|
|
}
|
|
context.current_protocol = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When — inspect protocol
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I inspect the LifecyclePlanRepositoryProtocol")
|
|
def step_inspect_plan_protocol(context: Any) -> None:
|
|
context.current_protocol = context.protocols["LifecyclePlanRepositoryProtocol"]
|
|
|
|
|
|
@when("I inspect the ActionRepositoryProtocol")
|
|
def step_inspect_action_protocol(context: Any) -> None:
|
|
context.current_protocol = context.protocols["ActionRepositoryProtocol"]
|
|
|
|
|
|
@when("I inspect the DecisionRepositoryProtocol")
|
|
def step_inspect_decision_protocol(context: Any) -> None:
|
|
context.current_protocol = context.protocols["DecisionRepositoryProtocol"]
|
|
|
|
|
|
@when("I inspect the ProjectRepositoryProtocol")
|
|
def step_inspect_project_protocol(context: Any) -> None:
|
|
context.current_protocol = context.protocols["ProjectRepositoryProtocol"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Then — protocol assertions
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then("it should be a runtime-checkable Protocol")
|
|
def step_is_runtime_checkable(context: Any) -> None:
|
|
"""Verify the protocol is decorated with @runtime_checkable."""
|
|
proto = context.current_protocol
|
|
assert hasattr(proto, "__protocol_attrs__") or hasattr(proto, "_is_protocol"), (
|
|
f"{proto} is not a Protocol"
|
|
)
|
|
is_runtime = getattr(proto, "_is_runtime_protocol", False)
|
|
assert is_runtime, f"{proto.__name__} is not @runtime_checkable"
|
|
|
|
|
|
@then('it should define a "{method_name}" method')
|
|
def step_defines_method(context: Any, method_name: str) -> None:
|
|
"""Verify the protocol defines the named method."""
|
|
proto = context.current_protocol
|
|
assert hasattr(proto, method_name), (
|
|
f"{proto.__name__} does not define method '{method_name}'"
|
|
)
|
|
assert callable(getattr(proto, method_name)), (
|
|
f"{proto.__name__}.{method_name} is not callable"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Given — infrastructure classes
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given("the infrastructure LifecyclePlanRepository class")
|
|
def step_given_plan_repo(context: Any) -> None:
|
|
from cleveragents.infrastructure.database.repositories import (
|
|
LifecyclePlanRepository,
|
|
)
|
|
|
|
context.infra_class = LifecyclePlanRepository
|
|
context.check_protocol = context.protocols["LifecyclePlanRepositoryProtocol"]
|
|
|
|
|
|
@given("the infrastructure ActionRepository class")
|
|
def step_given_action_repo(context: Any) -> None:
|
|
from cleveragents.infrastructure.database.repositories import ActionRepository
|
|
|
|
context.infra_class = ActionRepository
|
|
context.check_protocol = context.protocols["ActionRepositoryProtocol"]
|
|
|
|
|
|
@given("the infrastructure DecisionRepository class")
|
|
def step_given_decision_repo(context: Any) -> None:
|
|
from cleveragents.infrastructure.database.repositories import DecisionRepository
|
|
|
|
context.infra_class = DecisionRepository
|
|
context.check_protocol = context.protocols["DecisionRepositoryProtocol"]
|
|
|
|
|
|
@given("the infrastructure NamespacedProjectRepository class")
|
|
def step_given_project_repo(context: Any) -> None:
|
|
from cleveragents.infrastructure.database.repositories import (
|
|
NamespacedProjectRepository,
|
|
)
|
|
|
|
context.infra_class = NamespacedProjectRepository
|
|
context.check_protocol = context.protocols["ProjectRepositoryProtocol"]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When — check protocol satisfaction
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I check if it satisfies LifecyclePlanRepositoryProtocol")
|
|
def step_check_plan_protocol(context: Any) -> None:
|
|
context.protocol_check_result = issubclass(
|
|
context.infra_class, context.check_protocol
|
|
)
|
|
|
|
|
|
@when("I check if it satisfies ActionRepositoryProtocol")
|
|
def step_check_action_protocol(context: Any) -> None:
|
|
context.protocol_check_result = issubclass(
|
|
context.infra_class, context.check_protocol
|
|
)
|
|
|
|
|
|
@when("I check if it satisfies DecisionRepositoryProtocol")
|
|
def step_check_decision_protocol(context: Any) -> None:
|
|
context.protocol_check_result = issubclass(
|
|
context.infra_class, context.check_protocol
|
|
)
|
|
|
|
|
|
@when("I check if it satisfies ProjectRepositoryProtocol")
|
|
def step_check_project_protocol(context: Any) -> None:
|
|
context.protocol_check_result = issubclass(
|
|
context.infra_class, context.check_protocol
|
|
)
|
|
|
|
|
|
@then("the isinstance check should pass")
|
|
def step_isinstance_passes(context: Any) -> None:
|
|
assert context.protocol_check_result, (
|
|
f"{context.infra_class.__name__} does not satisfy "
|
|
f"{context.check_protocol.__name__}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When/Then — package exports
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I import from cleveragents.domain.repositories")
|
|
def step_import_domain_repositories(context: Any) -> None:
|
|
import cleveragents.domain.repositories as repo_pkg
|
|
|
|
context.repo_pkg = repo_pkg
|
|
|
|
|
|
@then("LifecyclePlanRepositoryProtocol should be available")
|
|
def step_plan_protocol_available(context: Any) -> None:
|
|
assert hasattr(context.repo_pkg, "LifecyclePlanRepositoryProtocol"), (
|
|
"LifecyclePlanRepositoryProtocol not exported from domain.repositories"
|
|
)
|
|
|
|
|
|
@then("ActionRepositoryProtocol should be available")
|
|
def step_action_protocol_available(context: Any) -> None:
|
|
assert hasattr(context.repo_pkg, "ActionRepositoryProtocol"), (
|
|
"ActionRepositoryProtocol not exported from domain.repositories"
|
|
)
|
|
|
|
|
|
@then("DecisionRepositoryProtocol should be available")
|
|
def step_decision_protocol_available(context: Any) -> None:
|
|
assert hasattr(context.repo_pkg, "DecisionRepositoryProtocol"), (
|
|
"DecisionRepositoryProtocol not exported from domain.repositories"
|
|
)
|
|
|
|
|
|
@then("ProjectRepositoryProtocol should be available")
|
|
def step_project_protocol_available(context: Any) -> None:
|
|
assert hasattr(context.repo_pkg, "ProjectRepositoryProtocol"), (
|
|
"ProjectRepositoryProtocol not exported from domain.repositories"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# When/Then — module path checks
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("I check the module path of LifecyclePlanRepositoryProtocol")
|
|
def step_check_plan_module(context: Any) -> None:
|
|
from cleveragents.domain.repositories import LifecyclePlanRepositoryProtocol
|
|
|
|
context.current_module = LifecyclePlanRepositoryProtocol.__module__
|
|
|
|
|
|
@when("I check the module path of ActionRepositoryProtocol")
|
|
def step_check_action_module(context: Any) -> None:
|
|
from cleveragents.domain.repositories import ActionRepositoryProtocol
|
|
|
|
context.current_module = ActionRepositoryProtocol.__module__
|
|
|
|
|
|
@when("I check the module path of DecisionRepositoryProtocol")
|
|
def step_check_decision_module(context: Any) -> None:
|
|
from cleveragents.domain.repositories import DecisionRepositoryProtocol
|
|
|
|
context.current_module = DecisionRepositoryProtocol.__module__
|
|
|
|
|
|
@when("I check the module path of ProjectRepositoryProtocol")
|
|
def step_check_project_module(context: Any) -> None:
|
|
from cleveragents.domain.repositories import ProjectRepositoryProtocol
|
|
|
|
context.current_module = ProjectRepositoryProtocol.__module__
|
|
|
|
|
|
@then('the module should be under "cleveragents.domain"')
|
|
def step_module_under_domain(context: Any) -> None:
|
|
assert context.current_module.startswith("cleveragents.domain"), (
|
|
f"Protocol module '{context.current_module}' is not under 'cleveragents.domain'"
|
|
)
|