ab8ed19aa7
CI / benchmark-publish (pull_request) Has been skipped
CI / quality (pull_request) Successful in 19s
CI / lint (pull_request) Failing after 22s
CI / build (pull_request) Successful in 27s
CI / typecheck (pull_request) Successful in 55s
CI / coverage (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Has been skipped
CI / security (pull_request) Successful in 1m7s
CI / unit_tests (pull_request) Has been cancelled
CI / integration_tests (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
Master's refactor(automation) commit removed the AutomationLevel enum and automation_level field from Plan. Update all behave steps, robot helpers, and feature scenarios that referenced the removed type: - cli_lifecycle_robot_alignment_steps.py: drop import and field assignment - persistence_robot_alignment_steps.py: drop import and field assignment - subplan_model_steps.py: drop import, parameter, and field assignment - subplan_model.feature: replace automation level scenario with profile check - helper_cli_lifecycle.py: drop import and field assignment - helper_persistence_lifecycle.py: drop import, field assignment, and assertion
160 lines
5.4 KiB
Python
160 lines
5.4 KiB
Python
"""Step definitions for persistence_robot_alignment.feature.
|
|
|
|
Mirrors the Robot restart-simulation flow to keep unit/integration
|
|
persistence expectations aligned. Uses file-backed SQLite so the
|
|
close-reopen cycle exercises real disk persistence.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import tempfile
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session
|
|
|
|
from cleveragents.domain.models.core.action import Action, ActionState
|
|
from cleveragents.domain.models.core.plan import (
|
|
NamespacedName,
|
|
Plan,
|
|
PlanIdentity,
|
|
PlanPhase,
|
|
PlanTimestamps,
|
|
ProcessingState,
|
|
ProjectLink,
|
|
)
|
|
from cleveragents.infrastructure.database.models import init_database
|
|
from cleveragents.infrastructure.database.repositories import (
|
|
ActionRepository,
|
|
LifecyclePlanRepository,
|
|
)
|
|
|
|
|
|
def _phase_from_str(value: str) -> PlanPhase:
|
|
"""Convert a lowercase phase string to PlanPhase enum."""
|
|
return PlanPhase(value)
|
|
|
|
|
|
def _state_from_str(value: str) -> ProcessingState:
|
|
"""Convert a lowercase state string to ProcessingState enum."""
|
|
return ProcessingState(value)
|
|
|
|
|
|
@given("a fresh file-backed persistence alignment database")
|
|
def step_fresh_db(context: Context) -> None:
|
|
tmp_path: str = tempfile.mktemp(suffix=".db")
|
|
db_url: str = f"sqlite:///{tmp_path}"
|
|
engine: Any = init_database(db_url)
|
|
session: Session = Session(bind=engine)
|
|
context._pra_db_path = tmp_path
|
|
context._pra_session = session
|
|
context._pra_engine = engine
|
|
|
|
|
|
@given('a persistence alignment action "{name}"')
|
|
def step_create_action(context: Context, name: str) -> None:
|
|
session: Session = context._pra_session
|
|
factory = lambda: session # noqa: E731
|
|
now: datetime = datetime(2026, 2, 1, tzinfo=UTC)
|
|
action = Action(
|
|
namespaced_name=NamespacedName.parse(name),
|
|
description="Alignment test action",
|
|
definition_of_done="Restart assertions pass",
|
|
strategy_actor="local/strat",
|
|
execution_actor="local/exec",
|
|
state=ActionState.AVAILABLE,
|
|
created_at=now,
|
|
updated_at=now,
|
|
)
|
|
repo = ActionRepository(session_factory=factory)
|
|
repo.create(action)
|
|
session.commit()
|
|
context._pra_action_name = name
|
|
|
|
|
|
@given(
|
|
'a persistence alignment plan "{plan_id}" in phase "{phase}" with state "{state}"'
|
|
)
|
|
def step_create_plan(context: Context, plan_id: str, phase: str, state: str) -> None:
|
|
session: Session = context._pra_session
|
|
factory = lambda: session # noqa: E731
|
|
now: datetime = datetime(2026, 3, 1, tzinfo=UTC)
|
|
plan = Plan(
|
|
identity=PlanIdentity(plan_id=plan_id, attempt=1),
|
|
namespaced_name=NamespacedName(namespace="local", name="aligned-plan"),
|
|
action_name=context._pra_action_name,
|
|
description="Alignment test plan",
|
|
definition_of_done="Alignment pass",
|
|
phase=_phase_from_str(phase),
|
|
processing_state=_state_from_str(state),
|
|
strategy_actor="local/strat",
|
|
execution_actor="local/exec",
|
|
project_links=[ProjectLink(project_name="local/proj-aligned")],
|
|
timestamps=PlanTimestamps(created_at=now, updated_at=now),
|
|
created_by="alignment-test",
|
|
tags=["alignment"],
|
|
reusable=True,
|
|
read_only=False,
|
|
)
|
|
repo = LifecyclePlanRepository(session_factory=factory)
|
|
repo.create(plan)
|
|
session.commit()
|
|
|
|
|
|
@when("the persistence alignment database is closed and reopened")
|
|
def step_close_reopen(context: Context) -> None:
|
|
context._pra_session.close()
|
|
context._pra_engine.dispose()
|
|
|
|
db_url: str = f"sqlite:///{context._pra_db_path}"
|
|
engine2: Any = create_engine(db_url, echo=False)
|
|
session2: Session = Session(bind=engine2)
|
|
context._pra_session = session2
|
|
context._pra_engine = engine2
|
|
|
|
|
|
@then('the persistence alignment plan "{plan_id}" should exist')
|
|
def step_plan_exists(context: Context, plan_id: str) -> None:
|
|
session: Session = context._pra_session
|
|
factory = lambda: session # noqa: E731
|
|
repo = LifecyclePlanRepository(session_factory=factory)
|
|
plan = repo.get(plan_id)
|
|
assert plan is not None, f"Plan {plan_id} not found after reopen"
|
|
context._pra_plan = plan
|
|
|
|
|
|
@then('the persistence alignment plan phase should be "{phase}"')
|
|
def step_check_phase(context: Context, phase: str) -> None:
|
|
assert context._pra_plan.phase == _phase_from_str(phase), (
|
|
f"Expected phase {phase}, got {context._pra_plan.phase}"
|
|
)
|
|
|
|
|
|
@then('the persistence alignment plan processing state should be "{state}"')
|
|
def step_check_state(context: Context, state: str) -> None:
|
|
assert context._pra_plan.processing_state == _state_from_str(state), (
|
|
f"Expected state {state}, got {context._pra_plan.processing_state}"
|
|
)
|
|
|
|
|
|
@then('the persistence alignment plan description should be "{desc}"')
|
|
def step_check_desc(context: Context, desc: str) -> None:
|
|
assert context._pra_plan.description == desc, (
|
|
f"Expected '{desc}', got '{context._pra_plan.description}'"
|
|
)
|
|
|
|
|
|
@then("the persistence alignment plan should have {count:d} project link")
|
|
def step_check_links(context: Context, count: int) -> None:
|
|
actual: int = len(context._pra_plan.project_links)
|
|
assert actual == count, f"Expected {count} links, got {actual}"
|
|
# Clean up temp file
|
|
tmp_path: str = context._pra_db_path
|
|
context._pra_session.close()
|
|
context._pra_engine.dispose()
|
|
Path(tmp_path).unlink(missing_ok=True)
|