Files
temp/features/steps/automation_profile_crud_coverage_steps.py

271 lines
10 KiB
Python

"""Step definitions for AutomationProfileService CRUD coverage tests."""
from __future__ import annotations
from behave import given, then, when
from behave.runner import Context
from cleveragents.application.services.automation_profile_service import (
AutomationProfileService,
)
from cleveragents.core.exceptions import (
NotFoundError,
ValidationError,
)
from cleveragents.domain.models.core.automation_profile import AutomationProfile
# -------------------------------------------------------------------
# Mock repository
# -------------------------------------------------------------------
class MockAutomationProfileRepository:
"""In-memory mock that implements the repository interface."""
def __init__(self) -> None:
self._store: dict[str, AutomationProfile] = {}
def get_by_name(self, name: str) -> AutomationProfile | None:
return self._store.get(name)
def list_all(self) -> list[AutomationProfile]:
return list(self._store.values())
def upsert(self, profile: AutomationProfile) -> None:
self._store[profile.name] = profile
def delete(self, name: str) -> None:
self._store.pop(name, None)
# -------------------------------------------------------------------
# Given steps
# -------------------------------------------------------------------
@given("a profile service with a mock repository")
def step_given_service_with_mock_repo(context: Context) -> None:
"""Create a service backed by an in-memory mock repository."""
context.mock_repo = MockAutomationProfileRepository()
context.profile_service = AutomationProfileService(
repo=context.mock_repo,
global_default="manual",
)
@given('the mock repository contains a profile named "{name}"')
def step_given_repo_contains_profile(context: Context, name: str) -> None:
"""Seed the mock repository with a custom profile."""
profile = AutomationProfile(
name=name,
description=f"Custom profile {name}",
)
context.mock_repo.upsert(profile)
@given("a profile service without a repository")
def step_given_service_without_repo(context: Context) -> None:
"""Create a service with no repository (repo=None)."""
context.profile_service = AutomationProfileService(
repo=None,
global_default="manual",
)
# -------------------------------------------------------------------
# When steps: get_profile from repo
# -------------------------------------------------------------------
@when('I fetch profile "{name}"')
def step_when_fetch_profile(context: Context, name: str) -> None:
"""Fetch a profile by name (may come from repo)."""
context.fetched_profile = context.profile_service.get_profile(name)
# -------------------------------------------------------------------
# When steps: list_profiles with repo
# -------------------------------------------------------------------
@when("I list all available profiles")
def step_when_list_available_profiles(context: Context) -> None:
"""List all profiles including custom ones from the repo."""
context.available_profiles = context.profile_service.list_profiles()
# -------------------------------------------------------------------
# When steps: create_profile
# -------------------------------------------------------------------
@when('I create a profile with name "{name}" and description "{description}"')
def step_when_create_profile(context: Context, name: str, description: str) -> None:
"""Create a custom profile with the given config."""
config = {"name": name, "description": description}
context.created_profile = context.profile_service.create_profile(config)
@when('I try to create a profile with builtin name "{name}"')
def step_when_try_create_builtin(context: Context, name: str) -> None:
"""Attempt to create a profile with a built-in name."""
context.crud_validation_error = None
try:
context.profile_service.create_profile({"name": name})
except ValidationError as exc:
context.crud_validation_error = exc
# -------------------------------------------------------------------
# When steps: update_profile
# -------------------------------------------------------------------
@when('I update profile "{name}" with description "{description}"')
def step_when_update_profile(context: Context, name: str, description: str) -> None:
"""Update an existing custom profile."""
config = {"description": description}
context.updated_profile = context.profile_service.update_profile(name, config)
@when('I try to update builtin profile "{name}"')
def step_when_try_update_builtin(context: Context, name: str) -> None:
"""Attempt to update a built-in profile."""
context.crud_validation_error = None
try:
context.profile_service.update_profile(name, {"description": "changed"})
except ValidationError as exc:
context.crud_validation_error = exc
@when('I try to update profile "{name}" without repo')
def step_when_try_update_no_repo(context: Context, name: str) -> None:
"""Attempt to update a profile when no repository is configured."""
context.crud_not_found_error = None
try:
context.profile_service.update_profile(name, {"description": "changed"})
except NotFoundError as exc:
context.crud_not_found_error = exc
# -------------------------------------------------------------------
# When steps: delete_profile
# -------------------------------------------------------------------
@when('I delete profile "{name}"')
def step_when_delete_profile(context: Context, name: str) -> None:
"""Delete a custom profile."""
context.profile_service.delete_profile(name)
@when('I try to delete builtin profile "{name}"')
def step_when_try_delete_builtin(context: Context, name: str) -> None:
"""Attempt to delete a built-in profile."""
context.crud_validation_error = None
try:
context.profile_service.delete_profile(name)
except ValidationError as exc:
context.crud_validation_error = exc
@when('I try to delete profile "{name}" without repo')
def step_when_try_delete_no_repo(context: Context, name: str) -> None:
"""Attempt to delete a profile when no repository is configured."""
context.crud_not_found_error = None
try:
context.profile_service.delete_profile(name)
except NotFoundError as exc:
context.crud_not_found_error = exc
# -------------------------------------------------------------------
# Then steps
# -------------------------------------------------------------------
@then('the fetched profile name should be "{expected}"')
def step_then_fetched_name(context: Context, expected: str) -> None:
"""Verify the fetched profile has the expected name."""
actual = context.fetched_profile.name
assert actual == expected, f"Expected '{expected}', got '{actual}'"
@then('the available profiles should include "{name}"')
def step_then_available_includes(context: Context, name: str) -> None:
"""Verify the available profiles list contains a given name."""
names = [p.name for p in context.available_profiles]
assert name in names, f"Expected '{name}' in list, got: {names}"
@then('the created profile name should be "{expected}"')
def step_then_created_name(context: Context, expected: str) -> None:
"""Verify the created profile has the expected name."""
actual = context.created_profile.name
assert actual == expected, f"Expected '{expected}', got '{actual}'"
@then('the created profile description should be "{expected}"')
def step_then_created_description(context: Context, expected: str) -> None:
"""Verify the created profile has the expected description."""
actual = context.created_profile.description
assert actual == expected, f"Expected '{expected}', got '{actual}'"
@then('the mock repository should contain "{name}"')
def step_then_repo_contains(context: Context, name: str) -> None:
"""Verify the mock repository has the profile stored."""
profile = context.mock_repo.get_by_name(name)
assert profile is not None, f"Expected '{name}' in repo, but not found"
@then('the mock repository should not contain "{name}"')
def step_then_repo_not_contains(context: Context, name: str) -> None:
"""Verify the mock repository does not have the profile."""
profile = context.mock_repo.get_by_name(name)
assert profile is None, f"Expected '{name}' not in repo, but found it"
@then('the updated profile name should be "{expected}"')
def step_then_updated_name(context: Context, expected: str) -> None:
"""Verify the updated profile has the expected name."""
actual = context.updated_profile.name
assert actual == expected, f"Expected '{expected}', got '{actual}'"
@then('the updated profile description should be "{expected}"')
def step_then_updated_description(context: Context, expected: str) -> None:
"""Verify the updated profile has the expected description."""
actual = context.updated_profile.description
assert actual == expected, f"Expected '{expected}', got '{actual}'"
@then("a crud validation error should be raised")
def step_then_crud_validation_error(context: Context) -> None:
"""Verify a ValidationError was raised."""
assert context.crud_validation_error is not None, (
"Expected ValidationError but none was raised"
)
@then('the crud validation error message should mention "{text}"')
def step_then_crud_validation_error_mentions(context: Context, text: str) -> None:
"""Check the ValidationError message contains the expected text."""
err = str(context.crud_validation_error)
assert text in err, f"Expected '{text}' in error, got: {err}"
@then("a crud not found error should be raised")
def step_then_crud_not_found_error(context: Context) -> None:
"""Verify a NotFoundError was raised."""
assert context.crud_not_found_error is not None, (
"Expected NotFoundError but none was raised"
)
@then('the crud not found error message should mention "{text}"')
def step_then_crud_not_found_error_mentions(context: Context, text: str) -> None:
"""Check the NotFoundError message contains the expected text."""
err = str(context.crud_not_found_error)
assert text in err, f"Expected '{text}' in error, got: {err}"