Publish curated ITWorx-ModelForge source from 990401737f96c8d46a9f7b0d84994516cadcb6a0
This commit is contained in:
1 parent
7082ab955a
commit
848f0d8f8a
434 files changed
+67298
-4315
No files matched your search
@@ -0,0 +1,316 @@
|
||||
"""Focused tests for RC stack inventory and stop-only retention guard."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from contextlib import redirect_stdout
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SPEC = importlib.util.spec_from_file_location(
|
||||
"rc_stack_retention", ROOT / "scripts" / "rc_stack_retention.py"
|
||||
)
|
||||
assert SPEC is not None and SPEC.loader is not None
|
||||
retention = importlib.util.module_from_spec(SPEC)
|
||||
SPEC.loader.exec_module(retention)
|
||||
|
||||
|
||||
PROJECT = "modelforge-validation-123271"
|
||||
NOW = datetime(2026, 9, 23, 10, 0, tzinfo=UTC)
|
||||
CREATED = "2026-09-20T10:00:00Z"
|
||||
|
||||
|
||||
class MockDocker:
|
||||
def __init__(self, containers: dict[str, dict[str, object]]) -> None:
|
||||
self.containers = containers
|
||||
self.calls: list[tuple[str, ...]] = []
|
||||
|
||||
def run(self, command: tuple[str, ...], **_kwargs: object) -> SimpleNamespace:
|
||||
self.assert_docker(command)
|
||||
args = tuple(command[1:])
|
||||
self.calls.append(args)
|
||||
if args[:4] == (
|
||||
"container", "ls", "--all", "--quiet"
|
||||
):
|
||||
return SimpleNamespace(
|
||||
returncode=0,
|
||||
stdout="\n".join(self.containers) + "\n",
|
||||
stderr="",
|
||||
)
|
||||
if args[:2] == ("container", "inspect"):
|
||||
identifier = args[2]
|
||||
return SimpleNamespace(
|
||||
returncode=0,
|
||||
stdout=json.dumps([self.containers[identifier]]),
|
||||
stderr="",
|
||||
)
|
||||
if args[:1] == ("stop",):
|
||||
identifiers = args[3:]
|
||||
for identifier in identifiers:
|
||||
state = self.containers[identifier]["State"]
|
||||
assert isinstance(state, dict)
|
||||
state["Status"] = "exited"
|
||||
return SimpleNamespace(returncode=0, stdout="\n".join(identifiers), stderr="")
|
||||
raise AssertionError(f"unexpected Docker command: {args}")
|
||||
|
||||
@staticmethod
|
||||
def assert_docker(command: tuple[str, ...]) -> None:
|
||||
if command[0] != "docker":
|
||||
raise AssertionError(f"unexpected executable: {command[0]}")
|
||||
|
||||
|
||||
def docker_container(
|
||||
identifier: str,
|
||||
project: str,
|
||||
service: str,
|
||||
*,
|
||||
status: str = "exited",
|
||||
source: str | None = None,
|
||||
created: str = CREATED,
|
||||
oneoff: str = "False",
|
||||
) -> dict[str, object]:
|
||||
mounts: list[dict[str, str]] = []
|
||||
if source is not None:
|
||||
mounts.append({"Type": "bind", "Source": source, "Destination": "/app/config"})
|
||||
return {
|
||||
"Id": identifier,
|
||||
"Name": f"/{project}-{service}-1",
|
||||
"Created": created,
|
||||
"Config": {
|
||||
"Labels": {
|
||||
retention.PROJECT_LABEL: project,
|
||||
retention.SERVICE_LABEL: service,
|
||||
retention.ONEOFF_LABEL: oneoff,
|
||||
}
|
||||
},
|
||||
"State": {"Status": status, "Running": status == "running"},
|
||||
"Mounts": mounts,
|
||||
}
|
||||
|
||||
|
||||
class RcStackRetentionTests(unittest.TestCase):
|
||||
def fixture(self, *, api_status: str = "exited", service: str = "postgres", oneoff: str = "False") -> tuple[MockDocker, tempfile.TemporaryDirectory[str]]:
|
||||
temporary = tempfile.TemporaryDirectory()
|
||||
config = Path(temporary.name) / "config"
|
||||
config.mkdir()
|
||||
containers = {
|
||||
"validation-api-id": docker_container("validation-api-id", PROJECT, "api", source=str(config)),
|
||||
"validation-db-id": docker_container(
|
||||
"validation-db-id", PROJECT, service, status="running", oneoff=oneoff
|
||||
),
|
||||
"prod-api-id": docker_container("prod-api-id", "modelforge", "api", status="running"),
|
||||
"other-api-id": docker_container(
|
||||
"other-api-id", "staging", "api", status="running"
|
||||
),
|
||||
}
|
||||
if api_status != "exited":
|
||||
containers["validation-api-id"]["State"] = {"Status": api_status, "Running": True}
|
||||
return MockDocker(containers), temporary
|
||||
|
||||
@staticmethod
|
||||
def write_evidence(path: Path, project: str = PROJECT) -> None:
|
||||
path.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": 1,
|
||||
"project": project,
|
||||
"retained": True,
|
||||
"evidence_reference": "acceptance-run-2026-09-20",
|
||||
}
|
||||
),
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
def test_inventory_scopes_validation_projects_and_flags_manifest_and_age(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
with patch.object(retention.subprocess, "run", docker.run):
|
||||
projects = retention.collect_projects(now=NOW, stale_after_hours=24)
|
||||
|
||||
self.assertEqual([row["project"] for row in projects], [PROJECT])
|
||||
self.assertTrue(projects[0]["stale"])
|
||||
self.assertEqual(len(projects[0]["missing_manifests"]), 1)
|
||||
self.assertEqual(projects[0]["missing_bind_sources"], [])
|
||||
self.assertNotIn("volumes", projects[0])
|
||||
self.assertNotIn("images", projects[0])
|
||||
self.assertTrue(all(call[:1] != ("stop",) for call in docker.calls))
|
||||
self.assertTrue(all(call[:1] != ("rm",) for call in docker.calls))
|
||||
|
||||
def test_inventory_ignores_non_validation_partial_compose_labels(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
docker.containers["unrelated-id"] = {
|
||||
"Id": "unrelated-id",
|
||||
"Name": "/projectbrain-worker-1",
|
||||
"Config": {"Labels": {retention.PROJECT_LABEL: "projectbrain"}},
|
||||
"State": {"Status": "exited"},
|
||||
}
|
||||
|
||||
with patch.object(retention.subprocess, "run", docker.run):
|
||||
projects = retention.collect_projects(now=NOW, stale_after_hours=24)
|
||||
|
||||
self.assertEqual([row["project"] for row in projects], [PROJECT])
|
||||
|
||||
def test_inventory_fails_closed_for_validation_container_missing_service_label(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
labels = docker.containers["validation-api-id"]["Config"]["Labels"]
|
||||
assert isinstance(labels, dict)
|
||||
del labels[retention.SERVICE_LABEL]
|
||||
|
||||
with patch.object(retention.subprocess, "run", docker.run), self.assertRaisesRegex(
|
||||
retention.RetentionError, "Compose service label is missing"
|
||||
):
|
||||
retention.collect_projects(now=NOW, stale_after_hours=24)
|
||||
|
||||
def test_default_cli_is_read_only(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
output = io.StringIO()
|
||||
with patch.object(retention.subprocess, "run", docker.run), redirect_stdout(output):
|
||||
status = retention.main([])
|
||||
|
||||
result = json.loads(output.getvalue())
|
||||
self.assertEqual(status, 0)
|
||||
self.assertEqual(result["mode"], "read-only")
|
||||
self.assertTrue(all(call[:1] != ("stop",) for call in docker.calls))
|
||||
|
||||
def test_docker_errors_and_ambiguous_inspection_fail_closed(self) -> None:
|
||||
failed = SimpleNamespace(returncode=1, stdout="", stderr="daemon unavailable")
|
||||
with patch.object(retention.subprocess, "run", return_value=failed), self.assertRaisesRegex(
|
||||
retention.RetentionError, "Docker command failed"
|
||||
):
|
||||
retention.collect_projects(now=NOW)
|
||||
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
docker.containers["validation-api-id"]["Id"] = "different-id"
|
||||
with patch.object(retention.subprocess, "run", docker.run), self.assertRaisesRegex(
|
||||
retention.RetentionError, "different container"
|
||||
):
|
||||
retention.collect_projects(now=NOW)
|
||||
|
||||
def test_project_validation_rejects_production_and_ambiguous_names(self) -> None:
|
||||
for project in (
|
||||
"modelforge",
|
||||
"modelforge-rc-prod-",
|
||||
"modelforge-validation-",
|
||||
"modelforge-validation-prod-",
|
||||
):
|
||||
with self.subTest(project=project), self.assertRaises(retention.RetentionError):
|
||||
retention.validate_project(project)
|
||||
|
||||
def test_stop_requires_explicit_exact_project_confirmation_and_evidence(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
empty_record = Path(temporary.name) / "empty.json"
|
||||
empty_record.write_text("", encoding="utf-8")
|
||||
with patch.object(retention.subprocess, "run", docker.run):
|
||||
with self.assertRaisesRegex(retention.RetentionError, "exact project name"):
|
||||
retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=None,
|
||||
confirm_no_active_jobs=True,
|
||||
evidence_record=Path("evidence.json"),
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
with self.assertRaisesRegex(retention.RetentionError, "nonempty regular file"):
|
||||
retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=PROJECT,
|
||||
confirm_no_active_jobs=True,
|
||||
evidence_record=empty_record,
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
mismatch = Path(temporary.name) / "mismatch.json"
|
||||
self.write_evidence(mismatch, project="modelforge-validation-someone-else")
|
||||
with patch.object(retention.subprocess, "run", docker.run), self.assertRaisesRegex(
|
||||
retention.RetentionError, "identify this project"
|
||||
):
|
||||
retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=PROJECT,
|
||||
confirm_no_active_jobs=True,
|
||||
evidence_record=mismatch,
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
self.assertEqual(docker.calls, [])
|
||||
|
||||
def test_stop_only_targets_inspected_project_container_ids(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
evidence = Path(temporary.name) / "evidence.json"
|
||||
self.write_evidence(evidence)
|
||||
with patch.object(retention.subprocess, "run", docker.run):
|
||||
result = retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=PROJECT,
|
||||
confirm_no_active_jobs=True,
|
||||
evidence_record=evidence,
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
|
||||
self.assertEqual(result["stopped_container_ids"], ["validation-db-id"])
|
||||
self.assertFalse(result["volumes_or_images_removed"])
|
||||
stop_calls = [call for call in docker.calls if call[:1] == ("stop",)]
|
||||
self.assertEqual(stop_calls, [("stop", "--time", "30", "validation-db-id")])
|
||||
self.assertTrue(all("prod-api-id" not in call for call in stop_calls))
|
||||
self.assertTrue(all(call[:1] not in {("rm",), ("rmi",), ("volume",)} for call in docker.calls))
|
||||
|
||||
def test_stop_fails_if_api_or_oneoff_job_is_running(self) -> None:
|
||||
for options, message in (
|
||||
({"api_status": "running"}, "API is running"),
|
||||
({"api_status": "restarting"}, "API is running"),
|
||||
({"oneoff": "True"}, "one-off job container"),
|
||||
({"service": "migrate"}, "service activity is unknown"),
|
||||
):
|
||||
with self.subTest(message=message):
|
||||
docker, temporary = self.fixture(**options)
|
||||
self.addCleanup(temporary.cleanup)
|
||||
evidence = Path(temporary.name) / "evidence.json"
|
||||
self.write_evidence(evidence)
|
||||
with patch.object(retention.subprocess, "run", docker.run), self.assertRaisesRegex(
|
||||
retention.RetentionError, message
|
||||
):
|
||||
retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=PROJECT,
|
||||
confirm_no_active_jobs=True,
|
||||
evidence_record=evidence,
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
self.assertTrue(all(call[:1] != ("stop",) for call in docker.calls))
|
||||
|
||||
def test_stop_requires_job_attestation(self) -> None:
|
||||
docker, temporary = self.fixture()
|
||||
self.addCleanup(temporary.cleanup)
|
||||
evidence = Path(temporary.name) / "evidence.json"
|
||||
self.write_evidence(evidence)
|
||||
with patch.object(retention.subprocess, "run", docker.run), self.assertRaisesRegex(
|
||||
retention.RetentionError, "--confirm-no-active-jobs"
|
||||
):
|
||||
retention.stop_project(
|
||||
PROJECT,
|
||||
confirm_stop=PROJECT,
|
||||
confirm_no_active_jobs=False,
|
||||
evidence_record=evidence,
|
||||
stale_after_hours=24,
|
||||
now=NOW,
|
||||
)
|
||||
self.assertEqual(docker.calls, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user