"""Step definitions for container_mount_flag.feature.""" from __future__ import annotations import json from typing import Any import typer from behave import given, then, when from behave.runner import Context from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from cleveragents.application.services.resource_registry_service import ( ResourceRegistryService, ) from cleveragents.cli.commands.resource import _parse_mount_spec from cleveragents.infrastructure.database.models import Base class _NoClose: """Proxy that suppresses ``close()`` on an in-memory SQLite session. Without this, SQLAlchemy's ``session.close()`` releases the connection, destroying the in-memory database between operations. """ __slots__ = ("_s",) def __init__(self, s: object) -> None: object.__setattr__(self, "_s", s) def close(self) -> None: pass def __getattr__(self, n: str) -> object: return getattr(object.__getattribute__(self, "_s"), n) @given("a resource registry service for mount_flag") def step_setup_registry(context: Context) -> None: engine = create_engine("sqlite:///:memory:", echo=False) Base.metadata.create_all(engine) session = sessionmaker(bind=engine, expire_on_commit=False)() wrapper = _NoClose(session) svc = ResourceRegistryService(session_factory=lambda: wrapper) svc.bootstrap_builtin_types() context.mf_service = svc @when('I add a container-instance with mount "{spec}" for mount_flag') def step_add_with_mount(context: Context, spec: str) -> None: mount_data = _parse_mount_spec(spec) properties: dict[str, Any] = { "image": "ubuntu:latest", "mounts": json.dumps([mount_data]), } res = context.mf_service.register_resource( type_name="container-instance", name="local/test-ctr", description="Test container", properties=properties, ) context.mf_resource = res context.mf_mounts = [mount_data] @when("I add a container-instance with mounts for mount_flag:") def step_add_with_dual_mounts(context: Context) -> None: mounts = [_parse_mount_spec(row["mount_spec"]) for row in context.table] properties: dict[str, Any] = { "image": "node:18", "mounts": json.dumps(mounts), } res = context.mf_service.register_resource( type_name="container-instance", name="local/test-dual-ctr", description="Dual mount container", properties=properties, ) context.mf_resource = res context.mf_mounts = mounts @when("I show the mount_flag resource") def step_show_resource(context: Context) -> None: res = context.mf_service.show_resource(context.mf_resource.name) context.mf_show_output = str(res.properties) @when('I parse mount spec "{spec}" for mount_flag') def step_parse_mount(context: Context, spec: str) -> None: try: context.mf_parsed = _parse_mount_spec(spec) context.mf_parse_error = None except (typer.BadParameter, ValueError) as exc: context.mf_parsed = None context.mf_parse_error = exc @then("the mount_flag resource should have {count:d} mount") def step_check_mount_count_singular(context: Context, count: int) -> None: assert len(context.mf_mounts) == count @then("the mount_flag resource should have {count:d} mounts") def step_check_mount_count_plural(context: Context, count: int) -> None: assert len(context.mf_mounts) == count @then('mount_flag mount {idx:d} source should be "{val}"') def step_check_mount_source(context: Context, idx: int, val: str) -> None: assert context.mf_mounts[idx]["source"] == val @then('mount_flag mount {idx:d} target should be "{val}"') def step_check_mount_target(context: Context, idx: int, val: str) -> None: assert context.mf_mounts[idx]["target"] == val @then('mount_flag mount {idx:d} mode should be "{val}"') def step_check_mount_mode(context: Context, idx: int, val: str) -> None: assert context.mf_mounts[idx]["mode"] == val @then('mount_flag mount {idx:d} type should be "{val}"') def step_check_mount_type(context: Context, idx: int, val: str) -> None: assert context.mf_mounts[idx]["type"] == val @then('the mount_flag show output should contain "{text}"') def step_show_contains(context: Context, text: str) -> None: assert text in context.mf_show_output, ( f"'{text}' not in show output: {context.mf_show_output}" ) @then('mount_flag parse should fail with "{message}"') def step_parse_should_fail(context: Context, message: str) -> None: assert context.mf_parse_error is not None, "Expected parse error" assert message.lower() in str(context.mf_parse_error).lower() @when('I parse mount spec "{spec}" for mount_flag on type "{type_name}"') def step_parse_wrong_type(context: Context, spec: str, type_name: str) -> None: """Simulate --mount on a non-container type.""" context.mf_wrong_type = type_name context.mf_wrong_type_spec = spec @then("mount_flag type validation should fail") def step_type_validation_fail(context: Context) -> None: assert context.mf_wrong_type != "container-instance", "Expected non-container type"