forked from HAL9000/cleveragents-core
49015c6bee
Add --execution-env-priority flag to 'project context set' command, enabling project-level execution environment priority per spec WF17. - Add execution_env_priority field to ContextConfig domain model - Validate flag value against ExecutionEnvPriority enum (fallback/override) - Persist in context_policy_json, preserving existing execution_environment - Display in 'project context show' Execution Environment section - Merge with existing blob to avoid overwriting previously set fields Tests: 9 Behave scenarios, 26 steps. ISSUES CLOSED: #1079
206 lines
6.7 KiB
Python
206 lines
6.7 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import tempfile
|
|
from typing import Any
|
|
|
|
from behave import given, then, when
|
|
from behave.runner import Context
|
|
|
|
from cleveragents.domain.models.core.plan import ExecutionEnvPriority
|
|
from cleveragents.domain.models.core.project import ContextConfig
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Domain model steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('I create a ContextConfig with execution_env_priority "{priority}"')
|
|
def step_create_cc_with_priority(context: Context, priority: str) -> None:
|
|
context.cc = ContextConfig(execution_env_priority=priority)
|
|
|
|
|
|
@given("I create a default ContextConfig")
|
|
def step_create_default_cc(context: Context) -> None:
|
|
context.cc = ContextConfig()
|
|
|
|
|
|
@then('the context config execution_env_priority should be "{expected}"')
|
|
def step_check_cc_priority(context: Context, expected: str) -> None:
|
|
assert context.cc.execution_env_priority == expected
|
|
|
|
|
|
@then("the context config execution_env_priority should be None")
|
|
def step_check_cc_priority_none(context: Context) -> None:
|
|
assert context.cc.execution_env_priority is None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Enum steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('the ExecutionEnvPriority enum should have "{value}"')
|
|
def step_check_eep_enum(context: Context, value: str) -> None:
|
|
assert value in [e.value for e in ExecutionEnvPriority]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CLI simulation helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _setup_test_db(context: Context, project_name: str) -> Any:
|
|
"""Create a temporary SQLite DB with the ns_projects table."""
|
|
from sqlalchemy import Column, String, Text, create_engine
|
|
from sqlalchemy.orm import Session, declarative_base, sessionmaker
|
|
|
|
fd, db_path = tempfile.mkstemp(suffix=".db")
|
|
os.close(fd)
|
|
context._eep_db_path = db_path
|
|
|
|
engine = create_engine(f"sqlite:///{db_path}")
|
|
Base = declarative_base()
|
|
|
|
class NsProject(Base): # type: ignore[misc]
|
|
__tablename__ = "ns_projects"
|
|
namespaced_name = Column(String(255), primary_key=True)
|
|
context_policy_json = Column(Text, nullable=True)
|
|
|
|
Base.metadata.create_all(engine)
|
|
factory = sessionmaker(bind=engine, expire_on_commit=False)
|
|
|
|
session: Session = factory()
|
|
session.execute(
|
|
NsProject.__table__.insert().values(
|
|
namespaced_name=project_name,
|
|
context_policy_json=None,
|
|
)
|
|
)
|
|
session.commit()
|
|
session.close()
|
|
|
|
context._eep_session_factory = factory
|
|
return factory
|
|
|
|
|
|
@given('I have a test project "{name}"')
|
|
def step_setup_test_project(context: Context, name: str) -> None:
|
|
_setup_test_db(context, name)
|
|
context._eep_project_name = name
|
|
|
|
|
|
def _load_blob(context: Context) -> dict[str, Any]:
|
|
"""Load the raw policy JSON from the test DB."""
|
|
from sqlalchemy import text
|
|
|
|
session = context._eep_session_factory()
|
|
try:
|
|
row = session.execute(
|
|
text(
|
|
"SELECT context_policy_json FROM ns_projects "
|
|
"WHERE namespaced_name = :ns"
|
|
),
|
|
{"ns": context._eep_project_name},
|
|
).fetchone()
|
|
if row is None or row[0] is None:
|
|
return {}
|
|
return json.loads(row[0])
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
def _save_blob(context: Context, blob: dict[str, Any]) -> None:
|
|
"""Save raw policy JSON to the test DB."""
|
|
from sqlalchemy import text
|
|
|
|
session = context._eep_session_factory()
|
|
try:
|
|
session.execute(
|
|
text(
|
|
"UPDATE ns_projects SET context_policy_json = :blob "
|
|
"WHERE namespaced_name = :ns"
|
|
),
|
|
{"blob": json.dumps(blob), "ns": context._eep_project_name},
|
|
)
|
|
session.commit()
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# CLI validation steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I call project context set with execution-env-priority "{priority}"')
|
|
def step_call_set_with_priority(context: Context, priority: str) -> None:
|
|
context._eep_error = None
|
|
try:
|
|
ExecutionEnvPriority(priority.lower())
|
|
# Valid -- persist it
|
|
blob = _load_blob(context)
|
|
blob["execution_env_priority"] = priority.lower()
|
|
_save_blob(context, blob)
|
|
except ValueError as exc:
|
|
context._eep_error = exc
|
|
|
|
|
|
@then("a CLI error should be raised for invalid priority")
|
|
def step_cli_error_raised(context: Context) -> None:
|
|
assert context._eep_error is not None
|
|
|
|
|
|
@then('the project priority should be persisted as "{expected}"')
|
|
def step_check_persisted_priority(context: Context, expected: str) -> None:
|
|
blob = _load_blob(context)
|
|
assert blob.get("execution_env_priority") == expected
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Persistence round-trip steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when('I set execution-environment "{env}" and priority "{priority}"')
|
|
def step_set_both(context: Context, env: str, priority: str) -> None:
|
|
blob = _load_blob(context)
|
|
blob["execution_environment"] = env
|
|
blob["execution_env_priority"] = priority
|
|
_save_blob(context, blob)
|
|
|
|
|
|
@then('the persisted blob should have execution_environment "{expected}"')
|
|
def step_blob_has_ee(context: Context, expected: str) -> None:
|
|
blob = _load_blob(context)
|
|
assert blob.get("execution_environment") == expected
|
|
|
|
|
|
@then('the persisted blob should have execution_env_priority "{expected}"')
|
|
def step_blob_has_eep(context: Context, expected: str) -> None:
|
|
blob = _load_blob(context)
|
|
assert blob.get("execution_env_priority") == expected
|
|
|
|
|
|
@when('I set execution-environment "{env}" first')
|
|
def step_set_ee_first(context: Context, env: str) -> None:
|
|
blob = _load_blob(context)
|
|
blob["execution_environment"] = env
|
|
_save_blob(context, blob)
|
|
|
|
|
|
@when('I set execution-env-priority "{priority}" second')
|
|
def step_set_eep_second(context: Context, priority: str) -> None:
|
|
blob = _load_blob(context)
|
|
blob["execution_env_priority"] = priority
|
|
_save_blob(context, blob)
|
|
|
|
|
|
@then('the persisted blob should still have execution_environment "{expected}"')
|
|
def step_blob_still_has_ee(context: Context, expected: str) -> None:
|
|
blob = _load_blob(context)
|
|
assert blob.get("execution_environment") == expected, (
|
|
f"Expected '{expected}', got '{blob.get('execution_environment')}'"
|
|
)
|