0ce2e14f2d
CI / status-check (push) Blocked by required conditions
CI / benchmark-regression (push) Has been skipped
CI / helm (push) Successful in 45s
CI / push-validation (push) Successful in 43s
CI / quality (push) Successful in 1m29s
CI / build (push) Successful in 1m7s
CI / lint (push) Successful in 1m39s
CI / typecheck (push) Successful in 1m54s
CI / security (push) Successful in 1m55s
CI / e2e_tests (push) Successful in 4m48s
CI / unit_tests (push) Successful in 5m45s
CI / integration_tests (push) Successful in 6m13s
CI / docker (push) Successful in 1m32s
CI / coverage (push) Failing after 19m57s
CI / benchmark-publish (push) Successful in 1h18m32s
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 56s
CI / quality (pull_request) Successful in 1m14s
CI / typecheck (pull_request) Successful in 1m24s
CI / security (pull_request) Successful in 1m25s
CI / helm (pull_request) Successful in 38s
CI / push-validation (pull_request) Successful in 38s
CI / build (pull_request) Successful in 1m6s
CI / benchmark-regression (pull_request) Failing after 1m36s
CI / unit_tests (pull_request) Successful in 4m51s
CI / integration_tests (pull_request) Successful in 4m15s
CI / e2e_tests (pull_request) Failing after 4m35s
CI / coverage (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
CI / status-check (pull_request) Has been cancelled
Apply ruff auto-format to resolve CI lint failure: long lines in subprocess.run() calls and list comprehensions were not wrapped to the project's line-length limit.
340 lines
12 KiB
Python
340 lines
12 KiB
Python
"""Step definitions for devcontainer_autodiscovery_wiring.feature.
|
|
|
|
Tests that discover_devcontainers() is correctly wired into
|
|
GitCheckoutHandler.discover_children() and FsDirectoryHandler.discover_children().
|
|
|
|
All steps use the ``dcwire`` prefix to avoid Behave AmbiguousStep errors.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
from behave import given, then, when
|
|
|
|
from cleveragents.domain.models.core.resource import PhysVirt, Resource
|
|
from cleveragents.resource.handlers.fs_directory import FsDirectoryHandler
|
|
from cleveragents.resource.handlers.git_checkout import GitCheckoutHandler
|
|
|
|
_VALID_ULID = "01JQDVHN5X5QJKBMZ3AP8Y4G7K"
|
|
_DEVCONTAINER_JSON = json.dumps({"name": "Test Dev Container", "image": "ubuntu:22.04"})
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def _make_git_resource(location: str) -> Resource:
|
|
"""Create a minimal git-checkout Resource."""
|
|
return Resource(
|
|
resource_id=_VALID_ULID,
|
|
name="test-repo",
|
|
resource_type_name="git-checkout",
|
|
classification=PhysVirt.PHYSICAL,
|
|
description="Test git checkout resource",
|
|
location=location,
|
|
)
|
|
|
|
|
|
def _make_fs_resource(location: str) -> Resource:
|
|
"""Create a minimal fs-directory Resource."""
|
|
return Resource(
|
|
resource_id=_VALID_ULID,
|
|
name="test-dir",
|
|
resource_type_name="fs-directory",
|
|
classification=PhysVirt.PHYSICAL,
|
|
description="Test filesystem directory resource",
|
|
location=location,
|
|
)
|
|
|
|
|
|
def _init_git_repo(tmpdir: str, extra_files: dict[str, str] | None = None) -> str:
|
|
"""Initialise a bare-minimum git repo with an initial commit."""
|
|
subprocess.run(["git", "init"], cwd=tmpdir, capture_output=True, check=True)
|
|
subprocess.run(
|
|
["git", "config", "user.email", "test@dcwire.dev"],
|
|
cwd=tmpdir,
|
|
capture_output=True,
|
|
check=True,
|
|
)
|
|
subprocess.run(
|
|
["git", "config", "user.name", "DCWire Test"],
|
|
cwd=tmpdir,
|
|
capture_output=True,
|
|
check=True,
|
|
)
|
|
subprocess.run(
|
|
["git", "config", "commit.gpgSign", "false"],
|
|
cwd=tmpdir,
|
|
capture_output=True,
|
|
check=True,
|
|
)
|
|
# Always create a README so there is at least one tracked file
|
|
readme = Path(tmpdir) / "README.md"
|
|
readme.write_text("# dcwire test\n")
|
|
|
|
if extra_files:
|
|
for rel_path, file_content in extra_files.items():
|
|
full = Path(tmpdir) / rel_path
|
|
full.parent.mkdir(parents=True, exist_ok=True)
|
|
full.write_text(file_content)
|
|
|
|
subprocess.run(["git", "add", "."], cwd=tmpdir, capture_output=True, check=True)
|
|
subprocess.run(
|
|
["git", "commit", "-m", "init"],
|
|
cwd=tmpdir,
|
|
capture_output=True,
|
|
check=True,
|
|
)
|
|
return tmpdir
|
|
|
|
|
|
def _create_devcontainer_dir(base: str, rel_path: str) -> None:
|
|
"""Create a devcontainer.json at the given relative path inside base."""
|
|
full = Path(base) / rel_path
|
|
full.parent.mkdir(parents=True, exist_ok=True)
|
|
full.write_text(_DEVCONTAINER_JSON)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GIVEN steps — git-checkout
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('dcwire a git repo with a ".devcontainer/devcontainer.json" file')
|
|
def step_dcwire_git_with_root_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-git-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/devcontainer.json")
|
|
_init_git_repo(tmpdir, {".devcontainer/devcontainer.json": _DEVCONTAINER_JSON})
|
|
ctx.dcwire_handler = GitCheckoutHandler()
|
|
ctx.dcwire_resource = _make_git_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given('dcwire a git repo with a ".devcontainer/api/devcontainer.json" named config')
|
|
def step_dcwire_git_with_named_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-git-named-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/api/devcontainer.json")
|
|
_init_git_repo(tmpdir, {".devcontainer/api/devcontainer.json": _DEVCONTAINER_JSON})
|
|
ctx.dcwire_handler = GitCheckoutHandler()
|
|
ctx.dcwire_resource = _make_git_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given("dcwire a git repo with no devcontainer configuration")
|
|
def step_dcwire_git_no_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-git-nodc-")
|
|
_init_git_repo(tmpdir)
|
|
ctx.dcwire_handler = GitCheckoutHandler()
|
|
ctx.dcwire_resource = _make_git_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given(
|
|
'dcwire a git repo with a subdirectory "src" and a ".devcontainer/devcontainer.json" file'
|
|
)
|
|
def step_dcwire_git_with_src_and_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-git-src-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/devcontainer.json")
|
|
_init_git_repo(
|
|
tmpdir,
|
|
{
|
|
"src/main.py": "print('hello')\n",
|
|
".devcontainer/devcontainer.json": _DEVCONTAINER_JSON,
|
|
},
|
|
)
|
|
ctx.dcwire_handler = GitCheckoutHandler()
|
|
ctx.dcwire_resource = _make_git_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given('dcwire a git repo with a root-level ".devcontainer.json" file')
|
|
def step_dcwire_git_with_root_level_devcontainer_json(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-git-rootdc-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer.json")
|
|
_init_git_repo(tmpdir, {".devcontainer.json": _DEVCONTAINER_JSON})
|
|
ctx.dcwire_handler = GitCheckoutHandler()
|
|
ctx.dcwire_resource = _make_git_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# GIVEN steps — fs-directory
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@given('dcwire a filesystem directory with a ".devcontainer/devcontainer.json" file')
|
|
def step_dcwire_fs_with_root_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-fs-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/devcontainer.json")
|
|
ctx.dcwire_handler = FsDirectoryHandler()
|
|
ctx.dcwire_resource = _make_fs_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given(
|
|
'dcwire a filesystem directory with a ".devcontainer/frontend/devcontainer.json" named config'
|
|
)
|
|
def step_dcwire_fs_with_named_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-fs-named-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/frontend/devcontainer.json")
|
|
ctx.dcwire_handler = FsDirectoryHandler()
|
|
ctx.dcwire_resource = _make_fs_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given("dcwire a filesystem directory with no devcontainer configuration")
|
|
def step_dcwire_fs_no_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-fs-nodc-")
|
|
ctx.dcwire_handler = FsDirectoryHandler()
|
|
ctx.dcwire_resource = _make_fs_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given(
|
|
'dcwire a filesystem directory with a subdirectory "lib" and a ".devcontainer/devcontainer.json" file'
|
|
)
|
|
def step_dcwire_fs_with_lib_and_devcontainer(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-fs-lib-")
|
|
lib_dir = Path(tmpdir) / "lib"
|
|
lib_dir.mkdir()
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer/devcontainer.json")
|
|
ctx.dcwire_handler = FsDirectoryHandler()
|
|
ctx.dcwire_resource = _make_fs_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
@given('dcwire a filesystem directory with a root-level ".devcontainer.json" file')
|
|
def step_dcwire_fs_with_root_level_devcontainer_json(ctx):
|
|
tmpdir = tempfile.mkdtemp(prefix="dcwire-fs-rootdc-")
|
|
_create_devcontainer_dir(tmpdir, ".devcontainer.json")
|
|
ctx.dcwire_handler = FsDirectoryHandler()
|
|
ctx.dcwire_resource = _make_fs_resource(tmpdir)
|
|
ctx.dcwire_result = None
|
|
ctx.dcwire_error = None
|
|
ctx.dcwire_last_dc_child = None
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# WHEN steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("dcwire I call discover_children on the git-checkout resource")
|
|
def step_dcwire_discover_git(ctx):
|
|
try:
|
|
ctx.dcwire_result = ctx.dcwire_handler.discover_children(
|
|
resource=ctx.dcwire_resource
|
|
)
|
|
except Exception as exc:
|
|
ctx.dcwire_error = exc
|
|
|
|
|
|
@when("dcwire I call discover_children on the fs-directory resource")
|
|
def step_dcwire_discover_fs(ctx):
|
|
try:
|
|
ctx.dcwire_result = ctx.dcwire_handler.discover_children(
|
|
resource=ctx.dcwire_resource
|
|
)
|
|
except Exception as exc:
|
|
ctx.dcwire_error = exc
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# THEN steps
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('dcwire the children include a "devcontainer-instance" resource named "{name}"')
|
|
def step_dcwire_has_devcontainer_child(ctx, name):
|
|
assert ctx.dcwire_error is None, f"Unexpected error: {ctx.dcwire_error}"
|
|
assert ctx.dcwire_result is not None, "Expected a list of children"
|
|
dc_children = [
|
|
r
|
|
for r in ctx.dcwire_result
|
|
if r.resource_type_name == "devcontainer-instance" and r.name == name
|
|
]
|
|
assert len(dc_children) == 1, (
|
|
f"Expected exactly one devcontainer-instance named '{name}', "
|
|
f"got {[r.name for r in ctx.dcwire_result if r.resource_type_name == 'devcontainer-instance']}"
|
|
)
|
|
ctx.dcwire_last_dc_child = dc_children[0]
|
|
|
|
|
|
@then('dcwire the children include a "fs-directory" resource named "{name}"')
|
|
def step_dcwire_has_fs_child(ctx, name):
|
|
assert ctx.dcwire_error is None, f"Unexpected error: {ctx.dcwire_error}"
|
|
assert ctx.dcwire_result is not None, "Expected a list of children"
|
|
fs_children = [
|
|
r
|
|
for r in ctx.dcwire_result
|
|
if r.resource_type_name == "fs-directory" and r.name == name
|
|
]
|
|
assert len(fs_children) == 1, (
|
|
f"Expected exactly one fs-directory named '{name}', "
|
|
f"got {[r.name for r in ctx.dcwire_result if r.resource_type_name == 'fs-directory']}"
|
|
)
|
|
|
|
|
|
@then('dcwire the devcontainer child has provisioning_state "{state}"')
|
|
def step_dcwire_has_provisioning_state(ctx, state):
|
|
assert ctx.dcwire_last_dc_child is not None, "No devcontainer child captured"
|
|
props = ctx.dcwire_last_dc_child.properties or {}
|
|
actual = props.get("provisioning_state")
|
|
assert actual == state, f"Expected provisioning_state='{state}', got '{actual}'"
|
|
|
|
|
|
@then("dcwire the devcontainer child has devcontainer_json_path set")
|
|
def step_dcwire_has_json_path(ctx):
|
|
assert ctx.dcwire_last_dc_child is not None, "No devcontainer child captured"
|
|
props = ctx.dcwire_last_dc_child.properties or {}
|
|
path_val = props.get("devcontainer_json_path")
|
|
assert path_val, f"Expected devcontainer_json_path to be set, got {path_val!r}"
|
|
assert "devcontainer.json" in path_val, (
|
|
f"Expected devcontainer_json_path to contain 'devcontainer.json', got {path_val!r}"
|
|
)
|
|
|
|
|
|
@then('dcwire the devcontainer child has config_name "{config_name}"')
|
|
def step_dcwire_has_config_name(ctx, config_name):
|
|
assert ctx.dcwire_last_dc_child is not None, "No devcontainer child captured"
|
|
props = ctx.dcwire_last_dc_child.properties or {}
|
|
actual = props.get("config_name")
|
|
assert actual == config_name, (
|
|
f"Expected config_name='{config_name}', got '{actual}'"
|
|
)
|
|
|
|
|
|
@then("dcwire no devcontainer-instance children are present")
|
|
def step_dcwire_no_devcontainer_children(ctx):
|
|
assert ctx.dcwire_error is None, f"Unexpected error: {ctx.dcwire_error}"
|
|
assert ctx.dcwire_result is not None, "Expected a list of children"
|
|
dc_children = [
|
|
r for r in ctx.dcwire_result if r.resource_type_name == "devcontainer-instance"
|
|
]
|
|
assert len(dc_children) == 0, (
|
|
f"Expected no devcontainer-instance children, got {[r.name for r in dc_children]}"
|
|
)
|