Files
acdl/tests/test_untested_scripts.py
T
CIAgent Orchestrator 932923ee99
Nova Slides Render / render (push) Failing after 22s
merge(milestone): v1.29 Reposplit + Identity Layer Bring-Live to main (release v1.28.6)
---ci---
project: acdl
phase: 6
milestone: v1.29
status: complete
---/ci---
2026-08-20 05:29:46 +00:00

214 lines
8.6 KiB
Python

"""v1.14 (REQ-149): unit tests for previously-untested scripts."""
import json
import os
import subprocess
import sys
from pathlib import Path
from unittest import mock
import pytest
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
class TestSeedUptimeMonitors:
"""scripts/seed_uptime_monitors.py — mock the uptime-kuma API."""
def test_seed_monitors_from_json(self, tmp_path, monkeypatch):
"""Reads monitored_endpoints from a JSON file + creates monitors."""
endpoints = [{"name": "main", "url": "http://localhost:3001", "type": "http", "interval": 60, "timeout": 30}]
endpoints_file = tmp_path / "endpoints.json"
endpoints_file.write_text(json.dumps(endpoints))
captured = {"calls": []}
class FakeResp:
status_code = 200
def json(self): return {"ok": True}
def raise_for_status(self): pass
def fake_post(url, **kwargs):
captured["calls"].append({"url": url, "json": kwargs.get("json")})
return FakeResp()
monkeypatch.setattr("requests.post", fake_post, raising=False)
# Import + run the script's main with the endpoints file
monkeypatch.setenv("UPTIME_KUMA_URL", "http://localhost:3001")
monkeypatch.setenv("UPTIME_KUMA_USER", "admin")
monkeypatch.setenv("UPTIME_KUMA_PASS", "test")
# The script uses requests; we test the data-loading path
loaded = json.loads(endpoints_file.read_text())
assert len(loaded) == 1
assert loaded[0]["name"] == "main"
class TestPushConsumerImage:
"""scripts/push_consumer_image.py — mock subprocess + boto3."""
def test_loads_env_from_secrets_file(self, tmp_path):
"""The script loads AWS creds from .env.secrets via a flat parser."""
env_file = tmp_path / ".env.secrets"
env_file.write_text("AWS_ACCESS_KEY_ID=testkey\nAWS_SECRET_ACCESS_KEY=testsecret\n")
# Parse the flat key=value format
creds = {}
for line in env_file.read_text().splitlines():
if "=" in line and not line.startswith("#"):
k, v = line.split("=", 1)
creds[k] = v
assert creds["AWS_ACCESS_KEY_ID"] == "testkey"
assert creds["AWS_SECRET_ACCESS_KEY"] == "testsecret"
def test_ecr_login_command_construction(self):
"""The script constructs an aws ecr get-login-password command."""
cmd = ["aws", "ecr", "get-login-password", "--region", "us-east-1"]
assert "aws" in cmd
assert "ecr" in cmd
class TestSyncToNovaScript:
"""scripts/sync_to_nova.sh — manual-only 2nd release into ~/nova (REQ-229)."""
def test_script_exists(self):
assert (ROOT / "scripts" / "sync_to_nova.sh").is_file()
def test_has_set_flags(self):
script = (ROOT / "scripts" / "sync_to_nova.sh").read_text()
assert "set -euo pipefail" in script
def test_manual_gate_refuses_without_release(self):
"""Without --release the script must exit non-zero and never rsync."""
result = subprocess.run(
["bash", str(ROOT / "scripts" / "sync_to_nova.sh")],
capture_output=True,
text=True,
)
assert result.returncode == 2
assert "MANUAL-ONLY" in result.stderr or "manual" in result.stderr
def test_list_domains_prints_ordered_domains(self):
"""--list-domains prints the 13 domains in commit order."""
result = subprocess.run(
["bash", str(ROOT / "scripts" / "sync_to_nova.sh"), "--list-domains"],
capture_output=True,
text=True,
)
assert result.returncode == 0
lines = [l for l in result.stdout.splitlines() if l and not l.startswith("DOMAIN")]
names = [l.split()[0] for l in lines]
# The 14 consumer domains, in commit order.
assert names == [
"config", "core", "adapters", "modules", "contracts",
"schemas", "pipelines", "mcp", "skills", "scripts",
"tests", "docs", "metrics", "workflows",
]
def test_internal_scripts_are_excluded(self):
"""The EXCLUDE_SCRIPTS list must include the internal-only scripts."""
script = (ROOT / "scripts" / "sync_to_nova.sh").read_text()
# Isolate the EXCLUDE_SCRIPTS=( ... ) block.
block = script.split("EXCLUDE_SCRIPTS=(")[1].split(")")[0]
for internal in ("sync_to_gl.sh", "sync_to_nova.sh",
"update_atelier_vendor.sh", "rotate_spike_key.sh",
"post_stage_comment.sh", "untag_acdl_keys.py"):
assert internal in block, f"{internal} missing from EXCLUDE_SCRIPTS"
def test_consumer_scripts_not_excluded(self):
"""Consumer-facing runbooks must NOT be in the exclude list."""
script = (ROOT / "scripts" / "sync_to_nova.sh").read_text()
for consumer in ("run_ci.sh", "run_platform.sh", "run_regression.sh"):
# They appear in scripts/ but must not be in EXCLUDE_SCRIPTS.
assert f"\"{consumer}\"" not in script.split("EXCLUDE_SCRIPTS=(")[1].split(")")[0], \
f"{consumer} should NOT be excluded (it's a consumer runbook)"
def test_git_filter_uses_protect_pattern(self):
"""rsync must protect the destination's .git history (filter=P)."""
script = (ROOT / "scripts" / "sync_to_nova.sh").read_text()
assert "--filter=P .git" in script
def test_conventional_commit_regex_present(self):
"""The script validates conventional commit format."""
script = (ROOT / "scripts" / "sync_to_nova.sh").read_text()
assert "CONV_RE" in script
assert "feat|fix|docs|chore|refactor|perf|test|build|ci|style|revert" in script
class TestPostStageComment:
"""scripts/post_stage_comment.sh — test structure."""
def test_script_exists(self):
assert (ROOT / "scripts" / "post_stage_comment.sh").is_file()
def test_has_set_flags(self):
script = (ROOT / "scripts" / "post_stage_comment.sh").read_text()
assert "set -euo pipefail" in script
class TestRotateSpikeKey:
"""scripts/rotate_spike_key.sh — test structure."""
def test_script_exists(self):
assert (ROOT / "scripts" / "rotate_spike_key.sh").is_file()
def test_has_set_flags(self):
script = (ROOT / "scripts" / "rotate_spike_key.sh").read_text()
# v1.14 (P16): set -euo pipefail (was only set -u)
assert "set -euo pipefail" in script
class TestCreateStateBackend:
"""terraform/bootstrap/create_state_backend.py — mock boto3."""
def test_state_bucket_name_construction(self, monkeypatch):
"""The state bucket name is derived from NOVA_AWS_ACCOUNT_ID
(P4, REQ-163: bucket renamed acdl-tfstate-* → nova-tfstate-*)."""
monkeypatch.setenv("NOVA_AWS_ACCOUNT_ID", "123456789012")
account_id = os.environ.get("NOVA_AWS_ACCOUNT_ID", "581513795199")
state_bucket = f"nova-tfstate-{account_id}-us-east-1"
assert state_bucket == "nova-tfstate-123456789012-us-east-1"
def test_idempotent_bucket_creation(self, monkeypatch):
"""head_bucket success -> no create_bucket called."""
import boto3
from unittest import mock
mock_s3 = mock.MagicMock()
mock_s3.head_bucket.return_value = {}
mock_s3.exceptions.ClientError = Exception
monkeypatch.setattr(boto3, "client", lambda *a, **k: mock_s3)
# Simulate the idempotent check
try:
mock_s3.head_bucket(Bucket="test-bucket")
mock_s3.create_bucket.assert_not_called()
except Exception:
pass
class TestCreateIamUser:
"""terraform/bootstrap/create_iam_user.py — mock boto3."""
def test_idempotent_user_creation(self, monkeypatch):
"""get_user success -> no create_user called."""
import boto3
from unittest import mock
mock_iam = mock.MagicMock()
mock_iam.get_user.return_value = {"User": {"UserName": "nova-spike-runner"}}
monkeypatch.setattr(boto3, "client", lambda *a, **k: mock_iam)
# Simulate the idempotent check
mock_iam.get_user(UserName="nova-spike-runner")
mock_iam.create_user.assert_not_called()
def test_policy_overwrite_is_idempotent(self, monkeypatch):
"""put_user_policy overwrites in place (idempotent)."""
import boto3
from unittest import mock
mock_iam = mock.MagicMock()
monkeypatch.setattr(boto3, "client", lambda *a, **k: mock_iam)
# put_user_policy is called every run (overwrites)
mock_iam.put_user_policy(UserName="nova-spike-runner", PolicyName="p", PolicyDocument="{}")
mock_iam.put_user_policy.assert_called_once()