mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-09-30 15:03:12 +00:00
244 lines
7.4 KiB
Python
244 lines
7.4 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""Tests for every hcp-run-guard refusal, exit-0, discard, and reconcile case."""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import importlib.util
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
SCRIPT = Path(__file__).with_name("hcp-run-guard.py")
|
||
|
|
WORKSPACE = "shoc-frontend-new-dev"
|
||
|
|
PATTERNS = [
|
||
|
|
"terraform/live/dev/**",
|
||
|
|
"terraform/live/modules/**",
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def load_module():
|
||
|
|
spec = importlib.util.spec_from_file_location("hcp_run_guard", SCRIPT)
|
||
|
|
module = importlib.util.module_from_spec(spec)
|
||
|
|
assert spec.loader is not None
|
||
|
|
spec.loader.exec_module(module)
|
||
|
|
return module
|
||
|
|
|
||
|
|
|
||
|
|
def workspace_payload(
|
||
|
|
*,
|
||
|
|
auto_apply: bool = False,
|
||
|
|
speculative: bool = True,
|
||
|
|
tags_regex: str | None = None,
|
||
|
|
trigger_patterns: list[str] | None = None,
|
||
|
|
locked: bool = False,
|
||
|
|
current_run: dict[str, Any] | None = None,
|
||
|
|
) -> dict[str, Any]:
|
||
|
|
return {
|
||
|
|
"data": {
|
||
|
|
"attributes": {
|
||
|
|
"auto-apply": auto_apply,
|
||
|
|
"speculative-enabled": speculative,
|
||
|
|
"vcs-repo": {"tags-regex": tags_regex},
|
||
|
|
"trigger-patterns": PATTERNS if trigger_patterns is None else trigger_patterns,
|
||
|
|
"locked": locked,
|
||
|
|
},
|
||
|
|
"relationships": {
|
||
|
|
"current-run": {"data": current_run},
|
||
|
|
},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def run_payload(*, status: str, plan_only: bool = False) -> dict[str, Any]:
|
||
|
|
return {"data": {"attributes": {"status": status, "plan-only": plan_only}}}
|
||
|
|
|
||
|
|
|
||
|
|
def check(module, payloads: dict[str, Any], posts: list | None = None):
|
||
|
|
calls: list[str] = []
|
||
|
|
|
||
|
|
def get(url: str) -> dict[str, Any]:
|
||
|
|
calls.append(url)
|
||
|
|
if url not in payloads:
|
||
|
|
raise AssertionError(f"unexpected GET {url}")
|
||
|
|
return payloads[url]
|
||
|
|
|
||
|
|
recorded: list[tuple[str, dict[str, Any]]] = []
|
||
|
|
|
||
|
|
def post(url: str, payload: dict[str, Any]) -> int:
|
||
|
|
recorded.append((url, payload))
|
||
|
|
if posts:
|
||
|
|
return posts.pop(0)
|
||
|
|
return 202
|
||
|
|
|
||
|
|
try:
|
||
|
|
code = module.check_and_discard(
|
||
|
|
workspace=WORKSPACE,
|
||
|
|
token="test-token",
|
||
|
|
get=get,
|
||
|
|
post=post,
|
||
|
|
)
|
||
|
|
return code, None, calls, recorded
|
||
|
|
except module.GuardError as exc:
|
||
|
|
return 1, str(exc), calls, recorded
|
||
|
|
|
||
|
|
|
||
|
|
def reconcile(module, outcome: str, payloads: dict[str, Any], run_id: str = "run-1"):
|
||
|
|
def get(url: str) -> dict[str, Any]:
|
||
|
|
if url not in payloads:
|
||
|
|
raise AssertionError(f"unexpected GET {url}")
|
||
|
|
return payloads[url]
|
||
|
|
|
||
|
|
try:
|
||
|
|
code = module.reconcile_apply(
|
||
|
|
run_id=run_id,
|
||
|
|
apply_outcome=outcome,
|
||
|
|
token="test-token",
|
||
|
|
get=get,
|
||
|
|
)
|
||
|
|
return code, None
|
||
|
|
except module.GuardError as exc:
|
||
|
|
return 1, str(exc)
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
module = load_module()
|
||
|
|
ws = f"https://app.terraform.io/api/v2/organizations/seahaven/workspaces/{WORKSPACE}"
|
||
|
|
run_url = "https://app.terraform.io/api/v2/runs/run-1"
|
||
|
|
discard_url = f"{run_url}/actions/discard"
|
||
|
|
failures: list[str] = []
|
||
|
|
|
||
|
|
def expect_refuse(name: str, payloads: dict[str, Any], fragment: str) -> None:
|
||
|
|
code, error, _, recorded = check(module, payloads)
|
||
|
|
if code != 1 or not error or fragment not in error:
|
||
|
|
failures.append(f"{name}: expected refuse containing {fragment!r}, got {code} {error}")
|
||
|
|
if recorded:
|
||
|
|
failures.append(f"{name}: discard was posted on a refusal")
|
||
|
|
|
||
|
|
expect_refuse(
|
||
|
|
"auto-apply",
|
||
|
|
{ws: workspace_payload(auto_apply=True)},
|
||
|
|
"auto-apply is on",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"speculative-off",
|
||
|
|
{ws: workspace_payload(speculative=False)},
|
||
|
|
"speculative plans are off",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"tags-regex",
|
||
|
|
{ws: workspace_payload(tags_regex="^v")},
|
||
|
|
"tag-based VCS triggering",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"wrong-patterns",
|
||
|
|
{ws: workspace_payload(trigger_patterns=["terraform/**"])},
|
||
|
|
"trigger-patterns must be",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"locked-without-run",
|
||
|
|
{ws: workspace_payload(locked=True, current_run=None)},
|
||
|
|
"locked without a current run",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"applying",
|
||
|
|
{
|
||
|
|
ws: workspace_payload(locked=True, current_run={"id": "run-1"}),
|
||
|
|
run_url: run_payload(status="applying"),
|
||
|
|
},
|
||
|
|
"wait, do not discard an apply",
|
||
|
|
)
|
||
|
|
expect_refuse(
|
||
|
|
"not-discardable",
|
||
|
|
{
|
||
|
|
ws: workspace_payload(locked=True, current_run={"id": "run-1"}),
|
||
|
|
run_url: run_payload(status="errored"),
|
||
|
|
},
|
||
|
|
"is not discardable",
|
||
|
|
)
|
||
|
|
|
||
|
|
code, error, _, recorded = check(module, {ws: workspace_payload(locked=False)})
|
||
|
|
if code != 0 or error is not None or recorded:
|
||
|
|
failures.append(f"unlocked: expected exit 0, got {code} {error} {recorded}")
|
||
|
|
|
||
|
|
code, error, _, recorded = check(
|
||
|
|
module,
|
||
|
|
{
|
||
|
|
ws: workspace_payload(locked=True, current_run={"id": "run-1"}),
|
||
|
|
run_url: run_payload(status="planned", plan_only=True),
|
||
|
|
},
|
||
|
|
)
|
||
|
|
if code != 0 or recorded:
|
||
|
|
failures.append(f"plan-only: expected exit 0 without discard, got {code} {recorded}")
|
||
|
|
|
||
|
|
code, error, _, recorded = check(
|
||
|
|
module,
|
||
|
|
{
|
||
|
|
ws: workspace_payload(locked=True, current_run={"id": "run-1"}),
|
||
|
|
run_url: run_payload(status="planned"),
|
||
|
|
},
|
||
|
|
)
|
||
|
|
if code != 0 or error is not None:
|
||
|
|
failures.append(f"discard: expected exit 0, got {code} {error}")
|
||
|
|
if not recorded or recorded[0][0] != discard_url:
|
||
|
|
failures.append(f"discard: posted {recorded}")
|
||
|
|
|
||
|
|
code, error, _, recorded = check(
|
||
|
|
module,
|
||
|
|
{
|
||
|
|
ws: workspace_payload(locked=True, current_run={"id": "run-1"}),
|
||
|
|
run_url: run_payload(status="policy_checked"),
|
||
|
|
},
|
||
|
|
posts=[409],
|
||
|
|
)
|
||
|
|
if code != 0:
|
||
|
|
failures.append(f"discard-409: expected exit 0, got {code} {error}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
module.check_and_discard(workspace=WORKSPACE, token="", get=lambda _url: {})
|
||
|
|
failures.append("missing-token: accepted empty token")
|
||
|
|
except module.GuardError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
code, error = reconcile(module, "success", {})
|
||
|
|
if code != 0:
|
||
|
|
failures.append(f"reconcile-success: expected 0, got {code} {error}")
|
||
|
|
|
||
|
|
code, error = reconcile(
|
||
|
|
module,
|
||
|
|
"failure",
|
||
|
|
{run_url: run_payload(status="applied")},
|
||
|
|
)
|
||
|
|
if code != 0:
|
||
|
|
failures.append(f"reconcile-applied: expected 0, got {code} {error}")
|
||
|
|
|
||
|
|
code, error = reconcile(
|
||
|
|
module,
|
||
|
|
"failure",
|
||
|
|
{run_url: run_payload(status="errored")},
|
||
|
|
)
|
||
|
|
if code != 1 or not error or "errored" not in error:
|
||
|
|
failures.append(f"reconcile-errored: expected refuse, got {code} {error}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
module.reconcile_apply(run_id="", apply_outcome="failure", token="test-token")
|
||
|
|
failures.append("reconcile-missing-run: accepted empty run id")
|
||
|
|
except module.GuardError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
try:
|
||
|
|
module.reconcile_apply(run_id="run-1", apply_outcome="failure", token="")
|
||
|
|
failures.append("reconcile-missing-token: accepted empty token")
|
||
|
|
except module.GuardError:
|
||
|
|
pass
|
||
|
|
|
||
|
|
if failures:
|
||
|
|
print("FAIL: hcp-run-guard cases failed", file=__import__("sys").stderr)
|
||
|
|
for item in failures:
|
||
|
|
print(f" - {item}", file=__import__("sys").stderr)
|
||
|
|
return 1
|
||
|
|
print("PASS: HCP run guard checks")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
raise SystemExit(main())
|