forked from HAL9000/cleveragents-core
e801eb1ee8
- Rewrite .forgejo/workflows/ci.yml to route all jobs through nox sessions - Fix coverage_report nox session: serial behave mode replaces broken parallel mode (22% -> 97% accuracy), raise fail-under from 85% to 97% - Pass posargs through format nox session for CI --check support - Add 11 CI workflow validation scenarios (Behave) + Robot smoke test + ASV bench - Add 108 new Behave scenarios covering 6 largest coverage gaps to reach 97%: yaml_template_engine, actor/config, actor/registry, message_router, context_analysis, context_service - Update docs/development/ci-cd.md with nox-based CI docs and 97% threshold - Restore implementation_plan.md verbose style, check off completed CI tasks Verified: 1673 scenarios pass, 97% coverage, lint clean, typecheck clean
288 lines
9.6 KiB
Python
288 lines
9.6 KiB
Python
"""Step definitions for actor_registry_new_coverage.feature."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock
|
|
|
|
from behave import given, then, when # type: ignore[import-untyped]
|
|
from behave.runner import Context # type: ignore[import-untyped]
|
|
|
|
from cleveragents.actor.config import ActorConfiguration
|
|
from cleveragents.actor.registry import ActorRegistry
|
|
from cleveragents.core.exceptions import ValidationError
|
|
from cleveragents.domain.models.core import Actor
|
|
from cleveragents.providers.registry import ProviderCapabilities, ProviderInfo
|
|
|
|
|
|
def _make_actor(name: str, provider: str = "openai", model: str = "gpt-4") -> Actor:
|
|
actor = MagicMock(spec=Actor)
|
|
actor.name = name
|
|
actor.provider = provider
|
|
actor.model = model
|
|
return actor
|
|
|
|
|
|
def _make_provider_info(
|
|
name: str, model: str, provider_type_value: str = "openai"
|
|
) -> ProviderInfo:
|
|
info = MagicMock(spec=ProviderInfo)
|
|
info.name = name
|
|
info.default_model = model
|
|
type_mock = MagicMock()
|
|
type_mock.value = provider_type_value
|
|
info.provider_type = type_mock
|
|
info.capabilities = ProviderCapabilities(
|
|
supports_streaming=True,
|
|
supports_tool_calls=True,
|
|
supports_vision=False,
|
|
supports_json_mode=True,
|
|
)
|
|
return info
|
|
|
|
|
|
def _make_settings(provider: str | None = None, model: str | None = None) -> MagicMock:
|
|
settings = MagicMock()
|
|
defaults = MagicMock()
|
|
defaults.provider = provider
|
|
defaults.model = model
|
|
settings.resolve_provider_defaults.return_value = defaults
|
|
return settings
|
|
|
|
|
|
@given("a stubbed actor registry")
|
|
def step_stubbed_registry(context: Context) -> None:
|
|
context.actor_service = MagicMock()
|
|
context.provider_registry = MagicMock()
|
|
context.settings = _make_settings()
|
|
context.registry = ActorRegistry(
|
|
actor_service=context.actor_service,
|
|
provider_registry=context.provider_registry,
|
|
settings=context.settings,
|
|
)
|
|
|
|
|
|
@then("the registry should have an actor service")
|
|
def step_has_actor_service(context: Context) -> None:
|
|
assert context.registry._actor_service is context.actor_service
|
|
|
|
|
|
@then("the registry should have a provider registry")
|
|
def step_has_provider_registry(context: Context) -> None:
|
|
assert context.registry._provider_registry is context.provider_registry
|
|
|
|
|
|
@then("the registry should have settings")
|
|
def step_has_settings(context: Context) -> None:
|
|
assert context.registry._settings is context.settings
|
|
|
|
|
|
@when('I format actor name for provider "{provider}" and model "{model}"')
|
|
def step_format_name(context: Context, provider: str, model: str) -> None:
|
|
context.result = context.registry._actor_name(provider, model)
|
|
|
|
|
|
@then('the actor name should be "{expected}"')
|
|
def step_assert_actor_name(context: Context, expected: str) -> None:
|
|
assert context.result == expected
|
|
|
|
|
|
@when(
|
|
'I build a graph descriptor for provider "{provider}" model "{model}" source "{source}"'
|
|
)
|
|
def step_build_descriptor(
|
|
context: Context, provider: str, model: str, source: str
|
|
) -> None:
|
|
context.descriptor = context.registry._build_graph_descriptor(
|
|
provider=provider, model=model, source=source
|
|
)
|
|
|
|
|
|
@then("the descriptor should contain provider model and source")
|
|
def step_assert_descriptor(context: Context) -> None:
|
|
d = context.descriptor
|
|
assert d["provider"] == "openai"
|
|
assert d["model"] == "gpt-4"
|
|
assert d["source"] == "test"
|
|
|
|
|
|
@when("I build a canonical blob from a config and raw blob")
|
|
def step_build_canonical(context: Context) -> None:
|
|
config = ActorConfiguration(
|
|
provider="openai", model="gpt-4", options={"temperature": 0.7}
|
|
)
|
|
raw: dict[str, Any] = {"extra": "data"}
|
|
context.blob = context.registry._canonical_blob(
|
|
config, raw, source="custom", default_graph=None
|
|
)
|
|
|
|
|
|
@then("the blob should contain provider model and source keys")
|
|
def step_assert_canonical_blob(context: Context) -> None:
|
|
b = context.blob
|
|
assert "provider" in b
|
|
assert "model" in b
|
|
assert "source" in b
|
|
|
|
|
|
@given("a stubbed actor registry with no configured providers")
|
|
def step_registry_no_providers(context: Context) -> None:
|
|
context.actor_service = MagicMock()
|
|
context.provider_registry = MagicMock()
|
|
context.provider_registry.get_configured_providers.return_value = []
|
|
context.settings = _make_settings()
|
|
context.registry = ActorRegistry(
|
|
actor_service=context.actor_service,
|
|
provider_registry=context.provider_registry,
|
|
settings=context.settings,
|
|
)
|
|
|
|
|
|
@when("I ensure built-in actors")
|
|
def step_ensure_built_ins(context: Context) -> None:
|
|
context.result = context.registry.ensure_built_in_actors()
|
|
|
|
|
|
@then("the registry result should be an empty list")
|
|
def step_assert_empty_list(context: Context) -> None:
|
|
assert context.result == []
|
|
|
|
|
|
@given("a stubbed actor registry with configured providers")
|
|
def step_registry_with_providers(context: Context) -> None:
|
|
context.actor_service = MagicMock()
|
|
info = _make_provider_info("openai", "gpt-4")
|
|
context.provider_registry = MagicMock()
|
|
context.provider_registry.get_configured_providers.return_value = [info]
|
|
context.actor_service.get_default_actor.return_value = None
|
|
|
|
actor = _make_actor("openai/gpt-4")
|
|
context.actor_service.upsert_actor.return_value = actor
|
|
|
|
context.settings = _make_settings()
|
|
context.registry = ActorRegistry(
|
|
actor_service=context.actor_service,
|
|
provider_registry=context.provider_registry,
|
|
settings=context.settings,
|
|
)
|
|
|
|
|
|
@then("the result should contain created actors")
|
|
def step_assert_actors_created(context: Context) -> None:
|
|
assert len(context.result) > 0
|
|
|
|
|
|
@then("the default actor should be set")
|
|
def step_assert_default_set(context: Context) -> None:
|
|
context.actor_service.set_default_actor.assert_called()
|
|
|
|
|
|
@given("a stubbed actor registry with configured providers and provider defaults")
|
|
def step_registry_with_defaults(context: Context) -> None:
|
|
context.actor_service = MagicMock()
|
|
info = _make_provider_info("anthropic", "claude-3", "anthropic")
|
|
context.provider_registry = MagicMock()
|
|
context.provider_registry.get_configured_providers.return_value = [info]
|
|
context.actor_service.get_default_actor.return_value = None
|
|
|
|
actor = _make_actor("anthropic/claude-3", provider="anthropic", model="claude-3")
|
|
context.actor_service.upsert_actor.return_value = actor
|
|
|
|
context.settings = _make_settings(provider="anthropic", model="claude-3")
|
|
context.registry = ActorRegistry(
|
|
actor_service=context.actor_service,
|
|
provider_registry=context.provider_registry,
|
|
settings=context.settings,
|
|
)
|
|
|
|
|
|
@then("the preferred provider should be set as default")
|
|
def step_assert_preferred_default(context: Context) -> None:
|
|
context.actor_service.set_default_actor.assert_called()
|
|
|
|
|
|
@when('I upsert a custom actor with name "local/test" and valid config')
|
|
def step_upsert_custom(context: Context) -> None:
|
|
actor = _make_actor("local/test")
|
|
context.actor_service.upsert_actor.return_value = actor
|
|
context.result = context.registry.upsert_actor(
|
|
name="local/test",
|
|
config_blob={"provider": "openai", "model": "gpt-4"},
|
|
)
|
|
|
|
|
|
@then("the actor should be persisted via the service")
|
|
def step_assert_persisted(context: Context) -> None:
|
|
assert context.actor_service.upsert_actor.called
|
|
|
|
|
|
@when("I upsert an actor with unsafe config but no unsafe flag")
|
|
def step_upsert_unsafe_no_flag(context: Context) -> None:
|
|
context.error = None
|
|
try:
|
|
context.registry.upsert_actor(
|
|
name="local/unsafe",
|
|
config_blob={"provider": "openai", "model": "gpt-4", "unsafe": True},
|
|
)
|
|
except ValidationError as exc:
|
|
context.error = exc
|
|
|
|
|
|
@then("a ValidationError should be raised")
|
|
def step_assert_validation_error(context: Context) -> None:
|
|
assert context.error is not None, "Expected ValidationError but none raised"
|
|
assert isinstance(context.error, ValidationError)
|
|
|
|
|
|
@when('I get actor "local/test"')
|
|
def step_get_actor(context: Context) -> None:
|
|
context.registry.get_actor("local/test")
|
|
|
|
|
|
@then("the actor service get_actor should be called")
|
|
def step_assert_get_called(context: Context) -> None:
|
|
context.actor_service.get_actor.assert_called_with("local/test")
|
|
|
|
|
|
@when("I list actors")
|
|
def step_list_actors(context: Context) -> None:
|
|
context.actor_service.list_actors.return_value = []
|
|
context.registry.list_actors()
|
|
|
|
|
|
@then("the actor service list_actors should be called")
|
|
def step_assert_list_called(context: Context) -> None:
|
|
context.actor_service.list_actors.assert_called()
|
|
|
|
|
|
@when('I remove actor "local/test"')
|
|
def step_remove_actor(context: Context) -> None:
|
|
context.registry.remove_actor("local/test")
|
|
|
|
|
|
@then("the actor service remove_actor should be called")
|
|
def step_assert_remove_called(context: Context) -> None:
|
|
context.actor_service.remove_actor.assert_called_with("local/test")
|
|
|
|
|
|
@when('I set default actor "local/test"')
|
|
def step_set_default(context: Context) -> None:
|
|
context.actor_service.set_default_actor.return_value = _make_actor("local/test")
|
|
context.registry.set_default_actor("local/test")
|
|
|
|
|
|
@then("the actor service set_default_actor should be called")
|
|
def step_assert_set_default_called(context: Context) -> None:
|
|
context.actor_service.set_default_actor.assert_called_with("local/test")
|
|
|
|
|
|
@when("I get default actor")
|
|
def step_get_default(context: Context) -> None:
|
|
context.actor_service.get_default_actor.return_value = None
|
|
context.registry.get_default_actor()
|
|
|
|
|
|
@then("the actor service get_default_actor should be called")
|
|
def step_assert_get_default_called(context: Context) -> None:
|
|
context.actor_service.get_default_actor.assert_called()
|