218 lines
12 KiB
Gherkin
218 lines
12 KiB
Gherkin
Feature: Devcontainer Activation, Stop and Rebuild
|
|
As a CleverAgents developer
|
|
I want devcontainers to be lazily activated and manually stopped or rebuilt
|
|
So that containers start on demand and can be controlled explicitly
|
|
|
|
# ── Lazy activation ────────────────────────────────────────
|
|
|
|
Scenario: Lazy activation succeeds on inactive container
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000010" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the container ID should be "aabbccddee0011223344"
|
|
And the workspace path should be "/workspaces/project"
|
|
And the runner should have received a devcontainer up call
|
|
|
|
Scenario: Lazy activation transitions to error on failure
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed activation
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000011" at "/workspace/project"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000011" should be "failed"
|
|
|
|
# ── Manual stop ────────────────────────────────────────────
|
|
|
|
Scenario: Stop transitions active container to stopped
|
|
Given a mock devcontainer CLI runner
|
|
And an active container "01TESTLIFECYCLE0000000020" with container ID "ctr-123"
|
|
When I stop container "01TESTLIFECYCLE0000000020"
|
|
Then the container state for "01TESTLIFECYCLE0000000020" should be "stopped"
|
|
And the runner should have received a docker stop call
|
|
|
|
# ── Rebuild ────────────────────────────────────────────────
|
|
|
|
Scenario: Rebuild transitions stopped container to active
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000030"
|
|
When I rebuild container "01TESTLIFECYCLE0000000030" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000030" should be "running"
|
|
|
|
Scenario: Rebuild transitions error container to active
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And an errored container "01TESTLIFECYCLE0000000031"
|
|
When I rebuild container "01TESTLIFECYCLE0000000031" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000031" should be "running"
|
|
|
|
# ── Argument validation ────────────────────────────────────
|
|
|
|
Scenario: Activate rejects empty resource_id
|
|
When I attempt to activate a container with empty resource_id
|
|
Then the activation should raise ValueError with "resource_id"
|
|
|
|
Scenario: Activate rejects empty workspace_folder
|
|
When I attempt to activate a container with empty workspace_folder
|
|
Then the activation should raise ValueError with "workspace_folder"
|
|
|
|
Scenario: Stop rejects empty resource_id
|
|
When I attempt to stop a container with empty resource_id
|
|
Then the stop should raise ValueError with "resource_id"
|
|
|
|
# ── Activate on already-running container ────────────────────
|
|
|
|
Scenario: Activate on already-running container raises ValueError
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And an active container "01TESTLIFECYCLE0000000140" with container ID "ctr-dup"
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000140" at "/workspace"
|
|
Then the activation should raise ValueError with "Invalid lifecycle transition"
|
|
|
|
# ── Stop container with no container_id ─────────────────────
|
|
|
|
Scenario: Stop container with no container_id skips docker stop
|
|
Given a mock devcontainer CLI runner
|
|
And an active container with no container_id "01TESTLIFECYCLE0000000150"
|
|
When I stop container "01TESTLIFECYCLE0000000150"
|
|
Then the container state for "01TESTLIFECYCLE0000000150" should be "stopped"
|
|
And the runner should not have received a docker stop call
|
|
|
|
# ── Activation fails when container_id is missing ────────────
|
|
|
|
Scenario: Activation fails when devcontainer up returns no container_id
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for activation with no container_id
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000160" at "/workspace"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000160" should be "failed"
|
|
|
|
# ── Direct rebuild_container coverage ───────────────────────
|
|
|
|
Scenario: rebuild_container directly stops then reactivates
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000090"
|
|
When I call rebuild_container for "01TESTLIFECYCLE0000000090" at "/workspace"
|
|
Then the container state for "01TESTLIFECYCLE0000000090" should be "running"
|
|
|
|
Scenario: rebuild_container rejects empty resource_id
|
|
When I call rebuild_container with empty resource_id
|
|
Then the rebuild should raise ValueError with "resource_id"
|
|
|
|
Scenario: rebuild_container rejects empty workspace_folder
|
|
When I call rebuild_container with empty workspace_folder
|
|
Then the rebuild should raise ValueError with "workspace_folder"
|
|
|
|
# ── Stop container error path coverage ─────────────────────
|
|
|
|
Scenario: stop_container transitions to error when docker stop raises
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured to raise exception on stop
|
|
And an active container "01TESTLIFECYCLE0000000110" with container ID "ctr-sf"
|
|
When I attempt to stop container "01TESTLIFECYCLE0000000110"
|
|
Then the stop should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000110" should be "failed"
|
|
|
|
# ── Activate generic exception path coverage ───────────────
|
|
|
|
Scenario: activate_container transitions to error on generic exception
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured to raise generic exception on up
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000120" at "/workspace"
|
|
Then the activation should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000120" should be "failed"
|
|
|
|
# ── Stop container non-zero return code (F2) ───────────────
|
|
|
|
Scenario: stop_container transitions to error when docker stop returns non-zero
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed docker stop
|
|
And an active container "01TESTLIFECYCLE0000000170" with container ID "ctr-rc1"
|
|
When I attempt to stop container "01TESTLIFECYCLE0000000170"
|
|
Then the stop should raise RuntimeError
|
|
And the container state for "01TESTLIFECYCLE0000000170" should be "failed"
|
|
|
|
# ── R20: workspace_folder path validation ──────────────────
|
|
|
|
Scenario: Activate rejects relative workspace_folder
|
|
When I attempt to activate a container with relative workspace_folder
|
|
Then the activation should raise ValueError with "absolute path"
|
|
|
|
# ── S2: Activation timeout transitions to failed ───────────
|
|
|
|
Scenario: Activation timeout transitions container to failed state
|
|
Given a mock devcontainer CLI runner that times out
|
|
And a lifecycle tracker for resource "01TESTLIFECYCLE0000000350"
|
|
When I attempt to activate the timed-out container
|
|
Then the activation should raise RuntimeError with "timed out"
|
|
And the tracker "01TESTLIFECYCLE0000000350" should be in "failed" state
|
|
|
|
# ── F4: Orphaned container cleanup on timeout ──────────────
|
|
|
|
Scenario: Activation timeout attempts to stop orphaned containers
|
|
Given a mock devcontainer CLI runner that times out with orphan containers
|
|
And a lifecycle tracker for resource "01TESTLIFECYCLE0000000360"
|
|
When I attempt to activate the timed-out container with orphan cleanup
|
|
Then the activation should raise RuntimeError with "timed out"
|
|
And the runner should have attempted orphan container cleanup
|
|
|
|
# ── F6: R10 fix explicit assertion (container_id clearing) ─
|
|
|
|
Scenario: Stopped container has container_id cleared
|
|
Given a mock devcontainer CLI runner
|
|
And an active container "01TESTLIFECYCLE0000000380" with container ID "ctr-r10"
|
|
When I stop container "01TESTLIFECYCLE0000000380"
|
|
Then the container state for "01TESTLIFECYCLE0000000380" should be "stopped"
|
|
And the container_id for "01TESTLIFECYCLE0000000380" should be None
|
|
And the workspace_path for "01TESTLIFECYCLE0000000380" should be None
|
|
|
|
Scenario: Failed activation clears container_id
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for failed activation
|
|
When I attempt to activate container "01TESTLIFECYCLE0000000381" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000381" should be "failed"
|
|
And the container_id for "01TESTLIFECYCLE0000000381" should be None
|
|
|
|
# ── R8-F2: stop_container early return on terminal state ────
|
|
|
|
Scenario: stop_container returns early when container already stopped
|
|
Given a mock devcontainer CLI runner
|
|
And a stopped container "01TESTLIFECYCLE0000000660"
|
|
When I stop container "01TESTLIFECYCLE0000000660" expecting early return
|
|
Then the container state for "01TESTLIFECYCLE0000000660" should be "stopped"
|
|
And the runner should not have received a docker stop call
|
|
|
|
Scenario: stop_container returns early when container already failed
|
|
Given a mock devcontainer CLI runner
|
|
And an errored container "01TESTLIFECYCLE0000000670"
|
|
When I stop container "01TESTLIFECYCLE0000000670" expecting early return
|
|
Then the container state for "01TESTLIFECYCLE0000000670" should be "failed"
|
|
And the runner should not have received a docker stop call
|
|
|
|
# ── F15: host_workspace_path stored on activation ───────────
|
|
|
|
Scenario: Activation stores host_workspace_path for health probes
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000800" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the host_workspace_path for "01TESTLIFECYCLE0000000800" should be "/workspace/project"
|
|
|
|
# ── F10: rebuild passes --reset-container flag ──────────────
|
|
|
|
Scenario: Rebuild passes --reset-container to devcontainer up
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
And a stopped container "01TESTLIFECYCLE0000000810"
|
|
When I call rebuild_container for "01TESTLIFECYCLE0000000810" at "/workspace/project"
|
|
Then the container state for "01TESTLIFECYCLE0000000810" should be "running"
|
|
And the devcontainer up call should include "--reset-container"
|
|
|
|
Scenario: Normal activation does not pass --reset-container
|
|
Given a mock devcontainer CLI runner
|
|
And the runner configured for successful activation
|
|
When I activate container "01TESTLIFECYCLE0000000811" at "/workspace/project"
|
|
Then the container state should be "running"
|
|
And the devcontainer up call should not include "--reset-container"
|