348 lines
12 KiB
Python
348 lines
12 KiB
Python
"""Step definitions for ProjectContextPolicy domain model tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
from pydantic import ValidationError
|
|
|
|
from cleveragents.domain.models.core.context_policy import (
|
|
ContextView,
|
|
ProjectContextPolicy,
|
|
)
|
|
|
|
# -------------------------------------------------------------------
|
|
# Helpers
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def _default_view(**overrides: Any) -> ContextView:
|
|
"""Create a ContextView with sensible defaults."""
|
|
defaults: dict[str, Any] = {}
|
|
defaults.update(overrides)
|
|
return ContextView(**defaults)
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Empty policy defaults
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when("I create an empty project context policy")
|
|
def step_create_empty_policy(context: Context) -> None:
|
|
context.policy = ProjectContextPolicy()
|
|
context.resolved_view = context.policy.default_view
|
|
|
|
|
|
@then("the default view should include all resources")
|
|
def step_default_includes_all_resources(context: Context) -> None:
|
|
assert context.resolved_view.include_resources == []
|
|
|
|
|
|
@then("the default view should include all paths")
|
|
def step_default_includes_all_paths(context: Context) -> None:
|
|
assert context.resolved_view.include_paths == []
|
|
|
|
|
|
@then("the default view should have no file size limit")
|
|
def step_default_no_file_size_limit(context: Context) -> None:
|
|
assert context.resolved_view.max_file_size is None
|
|
|
|
|
|
@then("the default view should have no total size limit")
|
|
def step_default_no_total_size_limit(context: Context) -> None:
|
|
assert context.resolved_view.max_total_size is None
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# View inheritance — Given steps
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@given("a policy with only a default view")
|
|
def step_policy_default_only(context: Context) -> None:
|
|
context.default_view = ContextView(
|
|
include_resources=["db-*"],
|
|
max_file_size=1024,
|
|
)
|
|
context.policy = ProjectContextPolicy(
|
|
default_view=context.default_view,
|
|
)
|
|
|
|
|
|
@given("a policy with default and strategize views")
|
|
def step_policy_default_and_strategize(context: Context) -> None:
|
|
context.default_view = ContextView(
|
|
include_resources=["db-*"],
|
|
)
|
|
context.strategize_view = ContextView(
|
|
include_resources=["db-*", "cache-*"],
|
|
)
|
|
context.policy = ProjectContextPolicy(
|
|
default_view=context.default_view,
|
|
strategize_view=context.strategize_view,
|
|
)
|
|
|
|
|
|
@given("a policy with default strategize and execute views")
|
|
def step_policy_default_strat_exec(context: Context) -> None:
|
|
context.default_view = ContextView(
|
|
include_resources=["db-*"],
|
|
)
|
|
context.strategize_view = ContextView(
|
|
include_resources=["db-*", "cache-*"],
|
|
)
|
|
context.execute_view = ContextView(
|
|
include_resources=["db-*", "cache-*", "api-*"],
|
|
)
|
|
context.policy = ProjectContextPolicy(
|
|
default_view=context.default_view,
|
|
strategize_view=context.strategize_view,
|
|
execute_view=context.execute_view,
|
|
)
|
|
|
|
|
|
@given("a policy with a custom execute view")
|
|
def step_policy_custom_execute(context: Context) -> None:
|
|
context.default_view = ContextView(
|
|
include_resources=["db-*"],
|
|
)
|
|
context.execute_view = ContextView(
|
|
include_resources=["all-*"],
|
|
max_file_size=2048,
|
|
)
|
|
context.policy = ProjectContextPolicy(
|
|
default_view=context.default_view,
|
|
execute_view=context.execute_view,
|
|
)
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# View inheritance — When/Then steps
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when('I resolve the view for phase "{phase}"')
|
|
def step_resolve_view(context: Context, phase: str) -> None:
|
|
context.resolved_view = context.policy.resolve_view(phase)
|
|
|
|
|
|
@then("the resolved view should be the default view")
|
|
def step_resolved_is_default(context: Context) -> None:
|
|
assert context.resolved_view is context.default_view
|
|
|
|
|
|
@then("the resolved view should be the strategize view")
|
|
def step_resolved_is_strategize(context: Context) -> None:
|
|
assert context.resolved_view is context.strategize_view
|
|
|
|
|
|
@then("the resolved view should be the execute view")
|
|
def step_resolved_is_execute(context: Context) -> None:
|
|
assert context.resolved_view is context.execute_view
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Invalid phase
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when('I try to resolve the view for phase "{phase}"')
|
|
def step_try_resolve_invalid_phase(context: Context, phase: str) -> None:
|
|
context.policy_error = None
|
|
try:
|
|
context.policy.resolve_view(phase)
|
|
except ValueError as exc:
|
|
context.policy_error = str(exc)
|
|
|
|
|
|
@then("a context policy error should be raised")
|
|
def step_policy_error_raised(context: Context) -> None:
|
|
assert context.policy_error is not None, "Expected an error but none was raised"
|
|
|
|
|
|
@then('the context policy error should mention "{text}"')
|
|
def step_error_mentions(context: Context, text: str) -> None:
|
|
assert text in context.policy_error, (
|
|
f"Expected '{text}' in error: {context.policy_error}"
|
|
)
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Include/exclude resources
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when('I create a context view with include resources "{patterns}"')
|
|
def step_create_view_include_resources(context: Context, patterns: str) -> None:
|
|
names = [p.strip() for p in patterns.split(",")]
|
|
context.ctx_view = ContextView(include_resources=names)
|
|
|
|
|
|
@then("the context view should have {count:d} include resources")
|
|
def step_view_include_resource_count(context: Context, count: int) -> None:
|
|
assert len(context.ctx_view.include_resources) == count
|
|
|
|
|
|
@when('I create a context view with exclude resources "{patterns}"')
|
|
def step_create_view_exclude_resources(context: Context, patterns: str) -> None:
|
|
names = [p.strip() for p in patterns.split(",")]
|
|
context.ctx_view = ContextView(exclude_resources=names)
|
|
|
|
|
|
@then("the context view should have {count:d} exclude resource")
|
|
def step_view_exclude_resource_count_singular(context: Context, count: int) -> None:
|
|
assert len(context.ctx_view.exclude_resources) == count
|
|
|
|
|
|
@when('I create a context view with include "{inc}" and exclude "{exc}"')
|
|
def step_create_view_include_exclude(context: Context, inc: str, exc: str) -> None:
|
|
context.ctx_view = ContextView(
|
|
include_resources=[inc],
|
|
exclude_resources=[exc],
|
|
)
|
|
|
|
|
|
@then("the context view should have {count:d} include resource")
|
|
def step_view_include_resource_count_singular(context: Context, count: int) -> None:
|
|
assert len(context.ctx_view.include_resources) == count
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Include/exclude paths
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when('I create a context view with include paths "{globs}"')
|
|
def step_create_view_include_paths(context: Context, globs: str) -> None:
|
|
paths = [g.strip() for g in globs.split(",")]
|
|
context.ctx_view = ContextView(include_paths=paths)
|
|
|
|
|
|
@then("the context view should have {count:d} include paths")
|
|
def step_view_include_path_count(context: Context, count: int) -> None:
|
|
assert len(context.ctx_view.include_paths) == count
|
|
|
|
|
|
@when('I create a context view with exclude paths "{globs}"')
|
|
def step_create_view_exclude_paths(context: Context, globs: str) -> None:
|
|
paths = [g.strip() for g in globs.split(",")]
|
|
context.ctx_view = ContextView(exclude_paths=paths)
|
|
|
|
|
|
@then("the context view should have {count:d} exclude paths")
|
|
def step_view_exclude_path_count(context: Context, count: int) -> None:
|
|
assert len(context.ctx_view.exclude_paths) == count
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Size limit validation
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when("I create a context view with max file size {size:d}")
|
|
def step_create_view_max_file_size(context: Context, size: int) -> None:
|
|
context.ctx_view = ContextView(max_file_size=size)
|
|
|
|
|
|
@then("the context view max file size should be {size:d}")
|
|
def step_view_max_file_size(context: Context, size: int) -> None:
|
|
assert context.ctx_view.max_file_size == size
|
|
|
|
|
|
@when("I create a context view with no file size limit")
|
|
def step_create_view_no_file_limit(context: Context) -> None:
|
|
context.ctx_view = ContextView()
|
|
|
|
|
|
@then("the context view max file size should be None")
|
|
def step_view_max_file_size_none(context: Context) -> None:
|
|
assert context.ctx_view.max_file_size is None
|
|
|
|
|
|
@when("I try to create a context view with max file size {size:d}")
|
|
def step_try_create_view_bad_file_size(context: Context, size: int) -> None:
|
|
context.policy_error = None
|
|
try:
|
|
ContextView(max_file_size=size)
|
|
except ValidationError as exc:
|
|
context.policy_error = str(exc)
|
|
|
|
|
|
@when("I create a context view with max total size {size:d}")
|
|
def step_create_view_max_total_size(context: Context, size: int) -> None:
|
|
context.ctx_view = ContextView(max_total_size=size)
|
|
|
|
|
|
@then("the context view max total size should be {size:d}")
|
|
def step_view_max_total_size(context: Context, size: int) -> None:
|
|
assert context.ctx_view.max_total_size == size
|
|
|
|
|
|
@when("I try to create a context view with max total size {size:d}")
|
|
def step_try_create_view_bad_total_size(context: Context, size: int) -> None:
|
|
context.policy_error = None
|
|
try:
|
|
ContextView(max_total_size=size)
|
|
except ValidationError as exc:
|
|
context.policy_error = str(exc)
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Serialization round-trip
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@given("a fully populated context policy")
|
|
def step_full_policy(context: Context) -> None:
|
|
context.policy = ProjectContextPolicy(
|
|
default_view=ContextView(
|
|
include_resources=["db-*"],
|
|
exclude_resources=["db-test"],
|
|
include_paths=["src/**"],
|
|
exclude_paths=["*.pyc"],
|
|
max_file_size=1048576,
|
|
max_total_size=10485760,
|
|
),
|
|
strategize_view=ContextView(
|
|
include_resources=["db-*", "cache-*"],
|
|
),
|
|
execute_view=ContextView(
|
|
include_paths=["src/**", "lib/**"],
|
|
),
|
|
apply_view=ContextView(
|
|
exclude_paths=["tests/**"],
|
|
),
|
|
)
|
|
|
|
|
|
@when("I serialize and deserialize the policy")
|
|
def step_roundtrip(context: Context) -> None:
|
|
json_str = context.policy.model_dump_json()
|
|
context.roundtrip_policy = ProjectContextPolicy.model_validate_json(json_str)
|
|
|
|
|
|
@then("the deserialized policy should match the original")
|
|
def step_roundtrip_match(context: Context) -> None:
|
|
assert context.roundtrip_policy == context.policy
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# ContextView model_dump keys
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
@when("I create a context view with defaults")
|
|
def step_create_default_view(context: Context) -> None:
|
|
context.ctx_view = ContextView()
|
|
context.ctx_view_dump = context.ctx_view.model_dump()
|
|
|
|
|
|
@then('the context view dump should have key "{key}"')
|
|
def step_view_dump_has_key(context: Context, key: str) -> None:
|
|
assert key in context.ctx_view_dump, (
|
|
f"Key '{key}' not in dump: {list(context.ctx_view_dump.keys())}"
|
|
)
|