forked from HAL9000/cleveragents-core
271 lines
10 KiB
Python
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}"
|