forked from HAL9000/cleveragents-core
31472b5413
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate. ISSUES CLOSED: #1232
590 lines
21 KiB
Python
590 lines
21 KiB
Python
"""Step definitions for plan_lifecycle_service_coverage_r3.feature.
|
|
|
|
Targets uncovered lines in PlanLifecycleService:
|
|
- Lines 318-323: _run_estimation exception handler
|
|
- Line 452: _resolve_actor_registry_entry dunder prefix
|
|
- Lines 640-659: list_actions persistence paths (namespace, list_all, cache,
|
|
state filter, DatabaseError fallback)
|
|
- Line 917: list_plans persisted cache refresh
|
|
- Lines 1592-1614: _complete_apply_if_queued branches
|
|
- Lines 1687-1741: try_auto_run full lifecycle auto-progression
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.application.services.plan_lifecycle_service import (
|
|
PlanLifecycleService,
|
|
)
|
|
from cleveragents.config.settings import Settings
|
|
from cleveragents.core.exceptions import DatabaseError
|
|
from cleveragents.domain.models.core.action import Action, ActionState
|
|
from cleveragents.domain.models.core.plan import (
|
|
AutomationProfileProvenance,
|
|
AutomationProfileRef,
|
|
NamespacedName,
|
|
Plan,
|
|
PlanIdentity,
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
ProjectLink,
|
|
)
|
|
|
|
# -----------------------------------------------------------------
|
|
# Helpers
|
|
# -----------------------------------------------------------------
|
|
|
|
|
|
def _make_settings(**overrides) -> Settings:
|
|
"""Create a Settings instance, clearing singleton."""
|
|
Settings._instance = None
|
|
return Settings(**overrides)
|
|
|
|
|
|
def _create_action_r3(context: Context, name: str, **kwargs) -> Action:
|
|
"""Create an action with sensible defaults."""
|
|
defaults = {
|
|
"name": name,
|
|
"description": f"Action for {name}",
|
|
"definition_of_done": "Tests pass",
|
|
"strategy_actor": "openai/gpt-4",
|
|
"execution_actor": "openai/gpt-4",
|
|
}
|
|
defaults.update(kwargs)
|
|
return context.plscov3_svc.create_action(**defaults)
|
|
|
|
|
|
def _advance_to_strat_complete_r3(context: Context) -> None:
|
|
"""Advance plan to Strategize/COMPLETE."""
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
svc.start_strategize(pid)
|
|
plan = svc.get_plan(pid)
|
|
plan.processing_state = ProcessingState.COMPLETE
|
|
context.plscov3_plan = plan
|
|
|
|
|
|
def _advance_to_exec_complete_r3(context: Context) -> None:
|
|
"""Advance plan from Strategize/COMPLETE to Execute/COMPLETE."""
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
svc.execute_plan(pid)
|
|
svc.start_execute(pid)
|
|
plan = svc.get_plan(pid)
|
|
plan.processing_state = ProcessingState.COMPLETE
|
|
context.plscov3_plan = plan
|
|
|
|
|
|
def _advance_to_apply_queued_r3(context: Context) -> None:
|
|
"""Advance plan to Apply/QUEUED."""
|
|
_advance_to_strat_complete_r3(context)
|
|
_advance_to_exec_complete_r3(context)
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
svc.apply_plan(pid)
|
|
context.plscov3_plan = svc.get_plan(pid)
|
|
|
|
|
|
def _advance_to_apply_processing_r3(context: Context) -> None:
|
|
"""Advance plan to Apply/PROCESSING."""
|
|
_advance_to_apply_queued_r3(context)
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
svc.start_apply(pid)
|
|
context.plscov3_plan = svc.get_plan(pid)
|
|
|
|
|
|
def _make_mock_action(name: str, state: ActionState = ActionState.AVAILABLE) -> Action:
|
|
"""Create a real Action domain object for test doubles."""
|
|
return Action(
|
|
namespaced_name=NamespacedName.parse(name),
|
|
description=f"Test action {name}",
|
|
definition_of_done="Done",
|
|
strategy_actor="openai/gpt-4",
|
|
execution_actor="openai/gpt-4",
|
|
state=state,
|
|
)
|
|
|
|
|
|
def _make_mock_plan(plan_id: str) -> Plan:
|
|
"""Create a real Plan domain object for test doubles."""
|
|
return Plan(
|
|
identity=PlanIdentity(plan_id=plan_id),
|
|
namespaced_name=NamespacedName.parse("local/test-plan"),
|
|
description="Test plan",
|
|
action_name="local/test",
|
|
phase=PlanPhase.STRATEGIZE,
|
|
processing_state=ProcessingState.QUEUED,
|
|
timestamps=PlanTimestamps(),
|
|
)
|
|
|
|
|
|
def _setup_mock_uow():
|
|
"""Create a mock UnitOfWork returning a mock context."""
|
|
uow = MagicMock()
|
|
ctx_mock = MagicMock()
|
|
uow.transaction.return_value.__enter__ = MagicMock(return_value=ctx_mock)
|
|
uow.transaction.return_value.__exit__ = MagicMock(return_value=False)
|
|
return uow, ctx_mock
|
|
|
|
|
|
# =================================================================
|
|
# Background
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a fresh service is ready")
|
|
def step_bg_fresh_service(context: Context) -> None:
|
|
settings = _make_settings()
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings)
|
|
context.plscov3_error = None
|
|
context.plscov3_plan = None
|
|
|
|
|
|
# =================================================================
|
|
# Estimation actor failure (lines 318-323)
|
|
# =================================================================
|
|
|
|
|
|
@given('plscov3 an action with estimation actor exists named "{name}" actor "{actor}"')
|
|
def step_create_action_est(context: Context, name: str, actor: str) -> None:
|
|
_create_action_r3(context, name, estimation_actor=actor)
|
|
|
|
|
|
@given('plscov3 the action "{name}" is used on project "{project}"')
|
|
def step_use_action(context: Context, name: str, project: str) -> None:
|
|
plan = context.plscov3_svc.use_action(
|
|
name, project_links=[ProjectLink(project_name=project)]
|
|
)
|
|
context.plscov3_plan = plan
|
|
|
|
|
|
@given("plscov3 the current plan is advanced to Strategize COMPLETE")
|
|
def step_advance_strat_complete(context: Context) -> None:
|
|
_advance_to_strat_complete_r3(context)
|
|
|
|
|
|
@when("plscov3 execute_plan is invoked with estimation stub raising")
|
|
def step_execute_estimation_raises(context: Context) -> None:
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
|
|
# Patch the EstimationStubActor import inside _run_estimation
|
|
patcher = patch(
|
|
"cleveragents.application.services.plan_executor.EstimationStubActor"
|
|
)
|
|
mock_cls = patcher.start()
|
|
context.add_cleanup(patcher.stop)
|
|
mock_instance = MagicMock()
|
|
mock_instance.estimate.side_effect = RuntimeError("estimation boom")
|
|
mock_cls.return_value = mock_instance
|
|
|
|
# Ensure plan has estimation_actor set so _run_estimation runs
|
|
plan = svc.get_plan(pid)
|
|
plan.estimation_actor = "est-actor"
|
|
|
|
try:
|
|
svc.execute_plan(pid)
|
|
except Exception as exc:
|
|
context.plscov3_error = exc
|
|
|
|
|
|
@then("plscov3 the current plan should be in Execute phase")
|
|
def step_assert_execute_phase(context: Context) -> None:
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
plan = context.plscov3_svc.get_plan(pid)
|
|
assert plan.phase == PlanPhase.EXECUTE, f"Expected Execute, got {plan.phase}"
|
|
|
|
|
|
# =================================================================
|
|
# _resolve_actor_registry_entry dunder prefix (line 452)
|
|
# =================================================================
|
|
|
|
|
|
@when('plscov3 resolve_actor_registry_entry is invoked for "{actor_name}"')
|
|
def step_resolve_dunder(context: Context, actor_name: str) -> None:
|
|
context.plscov3_resolved = context.plscov3_svc._resolve_actor_registry_entry(
|
|
actor_name
|
|
)
|
|
|
|
|
|
@then("plscov3 the resolved actor entry is None")
|
|
def step_assert_resolved_none(context: Context) -> None:
|
|
assert context.plscov3_resolved is None
|
|
|
|
|
|
# =================================================================
|
|
# list_actions persistence: namespace filter (lines 640-645)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a service with mock UoW is ready")
|
|
def step_svc_mock_uow(context: Context) -> None:
|
|
settings = _make_settings()
|
|
uow, ctx_mock = _setup_mock_uow()
|
|
ctx_mock.actions.get_by_namespace.return_value = []
|
|
ctx_mock.actions.list_all.return_value = []
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, unit_of_work=uow)
|
|
context.plscov3_ctx_mock = ctx_mock
|
|
context.plscov3_error = None
|
|
|
|
|
|
@when('plscov3 list_actions is invoked with namespace "{ns}" and state "{state}"')
|
|
def step_list_actions_ns_state(context: Context, ns: str, state: str) -> None:
|
|
state_enum = ActionState(state)
|
|
context.plscov3_actions_result = context.plscov3_svc.list_actions(
|
|
namespace=ns, state=state_enum
|
|
)
|
|
|
|
|
|
@then('plscov3 the mock get_by_namespace was invoked with "{ns}"')
|
|
def step_assert_get_by_namespace(context: Context, ns: str) -> None:
|
|
context.plscov3_ctx_mock.actions.get_by_namespace.assert_called_once()
|
|
args = context.plscov3_ctx_mock.actions.get_by_namespace.call_args
|
|
assert args[0][0] == ns, f"Expected namespace '{ns}', got {args}"
|
|
|
|
|
|
# =================================================================
|
|
# list_actions persistence: no namespace (line 648)
|
|
# =================================================================
|
|
|
|
|
|
@when("plscov3 list_actions is invoked without any namespace")
|
|
def step_list_actions_no_ns(context: Context) -> None:
|
|
context.plscov3_actions_result = context.plscov3_svc.list_actions()
|
|
|
|
|
|
@then("plscov3 the mock actions list_all was invoked")
|
|
def step_assert_actions_list_all(context: Context) -> None:
|
|
context.plscov3_ctx_mock.actions.list_all.assert_called_once()
|
|
|
|
|
|
# =================================================================
|
|
# list_actions persistence: cache refresh (lines 650-653)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a service with mock UoW returning DB actions is ready")
|
|
def step_svc_uow_db_actions(context: Context) -> None:
|
|
settings = _make_settings()
|
|
uow, ctx_mock = _setup_mock_uow()
|
|
db_action = _make_mock_action("local/db-action")
|
|
ctx_mock.actions.get_by_namespace.return_value = [db_action]
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, unit_of_work=uow)
|
|
context.plscov3_ctx_mock = ctx_mock
|
|
context.plscov3_db_action = db_action
|
|
context.plscov3_error = None
|
|
|
|
|
|
@when('plscov3 list_actions is invoked filtering by namespace "{ns}"')
|
|
def step_list_actions_filter_ns(context: Context, ns: str) -> None:
|
|
context.plscov3_actions_result = context.plscov3_svc.list_actions(namespace=ns)
|
|
|
|
|
|
@then("plscov3 the actions in-memory cache has the DB action")
|
|
def step_assert_cache_has_action(context: Context) -> None:
|
|
key = str(context.plscov3_db_action.namespaced_name)
|
|
assert key in context.plscov3_svc._actions, (
|
|
f"Expected '{key}' in cache, got {list(context.plscov3_svc._actions.keys())}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# list_actions persistence: state filter on list_all (lines 655-656)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a service with mock UoW returning mixed-state actions is ready")
|
|
def step_svc_uow_mixed_actions(context: Context) -> None:
|
|
settings = _make_settings()
|
|
uow, ctx_mock = _setup_mock_uow()
|
|
avail = _make_mock_action("local/avail-action", ActionState.AVAILABLE)
|
|
archived = _make_mock_action("local/archived-action", ActionState.ARCHIVED)
|
|
ctx_mock.actions.list_all.return_value = [avail, archived]
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, unit_of_work=uow)
|
|
context.plscov3_ctx_mock = ctx_mock
|
|
context.plscov3_error = None
|
|
|
|
|
|
@when('plscov3 list_actions is invoked without namespace but with state "{state}"')
|
|
def step_list_actions_state_only(context: Context, state: str) -> None:
|
|
state_enum = ActionState(state)
|
|
context.plscov3_actions_result = context.plscov3_svc.list_actions(state=state_enum)
|
|
|
|
|
|
@then("plscov3 only actions in available state are returned")
|
|
def step_assert_only_available(context: Context) -> None:
|
|
result = context.plscov3_actions_result
|
|
assert len(result) == 1, f"Expected 1 action, got {len(result)}"
|
|
assert result[0].state == ActionState.AVAILABLE
|
|
|
|
|
|
# =================================================================
|
|
# list_actions persistence: DatabaseError fallback (lines 658-659)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a service with failing DB actions is ready")
|
|
def step_svc_failing_db(context: Context) -> None:
|
|
settings = _make_settings()
|
|
uow, ctx_mock = _setup_mock_uow()
|
|
ctx_mock.actions.list_all.side_effect = DatabaseError("DB down")
|
|
ctx_mock.actions.get_by_namespace.side_effect = DatabaseError("DB down")
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, unit_of_work=uow)
|
|
context.plscov3_error = None
|
|
|
|
|
|
@given('plscov3 an in-memory action "{name}" is added')
|
|
def step_add_inmem_action(context: Context, name: str) -> None:
|
|
action = _make_mock_action(name)
|
|
context.plscov3_svc._actions[str(action.namespaced_name)] = action
|
|
context.plscov3_inmem_action = action
|
|
|
|
|
|
@when("plscov3 list_actions is invoked expecting DB fallback")
|
|
def step_list_actions_fallback(context: Context) -> None:
|
|
context.plscov3_actions_result = context.plscov3_svc.list_actions()
|
|
|
|
|
|
@then("plscov3 the fallback in-memory action is returned")
|
|
def step_assert_fallback_action(context: Context) -> None:
|
|
result = context.plscov3_actions_result
|
|
assert len(result) >= 1, f"Expected >= 1 actions, got {len(result)}"
|
|
names = [str(a.namespaced_name) for a in result]
|
|
expected = str(context.plscov3_inmem_action.namespaced_name)
|
|
assert expected in names, f"Expected '{expected}' in {names}"
|
|
|
|
|
|
# =================================================================
|
|
# list_plans persisted cache refresh (line 917)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 a service with mock UoW returning DB plans is ready")
|
|
def step_svc_uow_db_plans(context: Context) -> None:
|
|
settings = _make_settings()
|
|
uow, ctx_mock = _setup_mock_uow()
|
|
|
|
from ulid import ULID
|
|
|
|
plan_id = str(ULID())
|
|
db_plan = _make_mock_plan(plan_id)
|
|
ctx_mock.lifecycle_plans.list_all.return_value = [db_plan]
|
|
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, unit_of_work=uow)
|
|
context.plscov3_db_plan = db_plan
|
|
context.plscov3_error = None
|
|
|
|
|
|
@when("plscov3 list_plans is invoked")
|
|
def step_list_plans(context: Context) -> None:
|
|
context.plscov3_plans_result = context.plscov3_svc.list_plans()
|
|
|
|
|
|
@then("plscov3 the plans in-memory cache was refreshed")
|
|
def step_assert_plan_cache(context: Context) -> None:
|
|
pid = context.plscov3_db_plan.identity.plan_id
|
|
assert pid in context.plscov3_svc._plans, f"Expected '{pid}' in plan cache"
|
|
|
|
|
|
# =================================================================
|
|
# _complete_apply_if_queued - not in Apply phase (line 1592)
|
|
# =================================================================
|
|
|
|
|
|
@given('plscov3 a basic action named "{name}" is created')
|
|
def step_create_basic_action(context: Context, name: str) -> None:
|
|
_create_action_r3(context, name)
|
|
|
|
|
|
@when("plscov3 complete_apply_if_queued is invoked")
|
|
def step_call_caiq(context: Context) -> None:
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
try:
|
|
result = context.plscov3_svc._complete_apply_if_queued(pid)
|
|
context.plscov3_plan = result
|
|
except Exception as exc:
|
|
context.plscov3_error = exc
|
|
|
|
|
|
@then("plscov3 the current plan phase remains Strategize")
|
|
def step_assert_phase_strategize(context: Context) -> None:
|
|
assert context.plscov3_plan.phase == PlanPhase.STRATEGIZE, (
|
|
f"Expected Strategize, got {context.plscov3_plan.phase}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# _complete_apply_if_queued - not QUEUED (line 1594)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 the current plan is advanced to Apply PROCESSING")
|
|
def step_advance_apply_processing(context: Context) -> None:
|
|
_advance_to_apply_processing_r3(context)
|
|
|
|
|
|
@then("plscov3 the current plan remains in Apply PROCESSING")
|
|
def step_assert_apply_processing(context: Context) -> None:
|
|
plan = context.plscov3_plan
|
|
assert plan.phase == PlanPhase.APPLY, f"Expected Apply, got {plan.phase}"
|
|
assert plan.processing_state == ProcessingState.PROCESSING, (
|
|
f"Expected PROCESSING, got {plan.processing_state}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# _complete_apply_if_queued - async enabled (lines 1599-1603)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 an async-enabled service with job store is ready")
|
|
def step_svc_async(context: Context) -> None:
|
|
settings = _make_settings()
|
|
settings.async_enabled = True
|
|
job_store = MagicMock()
|
|
context.plscov3_svc = PlanLifecycleService(settings=settings, job_store=job_store)
|
|
context.plscov3_error = None
|
|
context.plscov3_plan = None
|
|
|
|
|
|
@given("plscov3 the current plan is advanced to Apply QUEUED")
|
|
def step_advance_apply_queued(context: Context) -> None:
|
|
_advance_to_apply_queued_r3(context)
|
|
|
|
|
|
@then("plscov3 the current plan remains in Apply QUEUED")
|
|
def step_assert_apply_queued(context: Context) -> None:
|
|
plan = context.plscov3_plan
|
|
assert plan.phase == PlanPhase.APPLY, f"Expected Apply, got {plan.phase}"
|
|
assert plan.processing_state == ProcessingState.QUEUED, (
|
|
f"Expected QUEUED, got {plan.processing_state}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# _complete_apply_if_queued - exception path (lines 1608-1614)
|
|
# =================================================================
|
|
|
|
|
|
@when("plscov3 complete_apply_if_queued is invoked with start_apply raising")
|
|
def step_caiq_start_apply_raises(context: Context) -> None:
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
|
|
patcher = patch.object(svc, "start_apply", side_effect=RuntimeError("boom"))
|
|
patcher.start()
|
|
context.add_cleanup(patcher.stop)
|
|
|
|
patcher2 = patch.object(svc, "_cleanup_devcontainers")
|
|
patcher2.start()
|
|
context.add_cleanup(patcher2.stop)
|
|
|
|
try:
|
|
result = svc._complete_apply_if_queued(pid)
|
|
context.plscov3_plan = result
|
|
except Exception as exc:
|
|
context.plscov3_error = exc
|
|
|
|
|
|
@then("plscov3 the current plan processing state is ERRORED")
|
|
def step_assert_errored(context: Context) -> None:
|
|
plan = context.plscov3_plan
|
|
assert plan.processing_state == ProcessingState.ERRORED, (
|
|
f"Expected ERRORED, got {plan.processing_state}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# try_auto_run - terminal plan (lines 1687-1690)
|
|
# =================================================================
|
|
|
|
|
|
@given("plscov3 the current plan is forced to CANCELLED state")
|
|
def step_force_cancelled(context: Context) -> None:
|
|
context.plscov3_plan.processing_state = ProcessingState.CANCELLED
|
|
|
|
|
|
@when("plscov3 try_auto_run is invoked")
|
|
def step_call_try_auto_run(context: Context) -> None:
|
|
pid = context.plscov3_plan.identity.plan_id
|
|
svc = context.plscov3_svc
|
|
|
|
patcher = patch.object(svc, "_cleanup_devcontainers")
|
|
patcher.start()
|
|
context.add_cleanup(patcher.stop)
|
|
|
|
try:
|
|
result = svc.try_auto_run(pid)
|
|
context.plscov3_plan = result
|
|
except Exception as exc:
|
|
context.plscov3_error = exc
|
|
|
|
|
|
@then("plscov3 the current plan is still CANCELLED")
|
|
def step_assert_still_cancelled(context: Context) -> None:
|
|
plan = context.plscov3_plan
|
|
assert plan.processing_state == ProcessingState.CANCELLED, (
|
|
f"Expected CANCELLED, got {plan.processing_state}"
|
|
)
|
|
|
|
|
|
# =================================================================
|
|
# try_auto_run - full auto with CI profile (lines 1697-1741)
|
|
# =================================================================
|
|
|
|
|
|
@given('plscov3 an action with ci automation profile named "{name}" is created')
|
|
def step_create_ci_action(context: Context, name: str) -> None:
|
|
_create_action_r3(context, name, automation_profile="ci")
|
|
|
|
|
|
@given(
|
|
'plscov3 that profiled action "{name}" is used on project "{project}" with profile "{profile}"'
|
|
)
|
|
def step_use_profiled_action(
|
|
context: Context, name: str, project: str, profile: str
|
|
) -> None:
|
|
plan = context.plscov3_svc.use_action(
|
|
name, project_links=[ProjectLink(project_name=project)]
|
|
)
|
|
plan.automation_profile = AutomationProfileRef(
|
|
profile_name=profile,
|
|
provenance=AutomationProfileProvenance.ACTION,
|
|
)
|
|
context.plscov3_plan = plan
|
|
|
|
|
|
@then("plscov3 the current plan has reached terminal APPLIED")
|
|
def step_assert_terminal_applied(context: Context) -> None:
|
|
assert context.plscov3_error is None, f"Unexpected error: {context.plscov3_error}"
|
|
plan = context.plscov3_plan
|
|
assert plan.processing_state == ProcessingState.APPLIED, (
|
|
f"Expected APPLIED, got {plan.processing_state}"
|
|
)
|
|
assert plan.phase == PlanPhase.APPLY, f"Expected Apply phase, got {plan.phase}"
|
|
|
|
|
|
# =================================================================
|
|
# try_auto_run - strategize only with supervised profile (1697-1709)
|
|
# =================================================================
|
|
|
|
|
|
@given('plscov3 an action with supervised automation profile named "{name}" is created')
|
|
def step_create_supervised_action(context: Context, name: str) -> None:
|
|
_create_action_r3(context, name, automation_profile="supervised")
|
|
|
|
|
|
@then("plscov3 the current plan is in Strategize COMPLETE")
|
|
def step_assert_strat_complete(context: Context) -> None:
|
|
plan = context.plscov3_plan
|
|
assert plan.phase == PlanPhase.STRATEGIZE, f"Expected Strategize, got {plan.phase}"
|
|
assert plan.processing_state == ProcessingState.COMPLETE, (
|
|
f"Expected COMPLETE, got {plan.processing_state}"
|
|
)
|