feat(cfold): canonicalize custom test layout
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
@ -24,13 +24,12 @@ def _mk_synthetic(tmp_path, monkeypatch, approved=True):
|
||||
"""A synthetic recipe dir exercising EVERY manifest surface, plus a repo-local tests dir.
|
||||
|
||||
cc-ci side: meta (2 data keys + 1 hook key non-default), ops.py (2 pre-ops), install_steps.sh,
|
||||
compose.ccci.yml, test_backup.py overlay, 2 functional + 1 playwright custom tests.
|
||||
repo-local side: test_restore.py overlay + 1 functional custom test (visible iff approved, HC2).
|
||||
compose.ccci.yml, test_backup.py overlay, 3 custom tests.
|
||||
repo-local side: test_restore.py overlay + 1 custom test (visible iff approved, HC2).
|
||||
"""
|
||||
ccci_root = tmp_path / "cc-ci-tests"
|
||||
d = ccci_root / RECIPE
|
||||
(d / "functional").mkdir(parents=True)
|
||||
(d / "playwright").mkdir()
|
||||
(d / "custom").mkdir(parents=True)
|
||||
(d / "recipe_meta.py").write_text(
|
||||
"HTTP_TIMEOUT = 600\n"
|
||||
"DEPS = ['keycloak']\n"
|
||||
@ -41,17 +40,17 @@ def _mk_synthetic(tmp_path, monkeypatch, approved=True):
|
||||
(d / "install_steps.sh").write_text("#!/usr/bin/env bash\n")
|
||||
(d / "compose.ccci.yml").write_text("version: '3.8'\n")
|
||||
(d / "test_backup.py").write_text("# lifecycle overlay\n")
|
||||
(d / "functional" / "test_a.py").write_text("# custom\n")
|
||||
(d / "functional" / "test_b.py").write_text("# custom\n")
|
||||
(d / "playwright" / "test_ui.py").write_text("# custom\n")
|
||||
(d / "custom" / "test_a.py").write_text("# custom\n")
|
||||
(d / "custom" / "test_b.py").write_text("# custom\n")
|
||||
(d / "custom" / "test_ui.py").write_text("# custom\n")
|
||||
|
||||
rl = tmp_path / "repo-local"
|
||||
(rl / "functional").mkdir(parents=True)
|
||||
(rl / "functional" / "test_c.py").write_text("# repo-local custom\n")
|
||||
(rl / "custom").mkdir(parents=True)
|
||||
(rl / "custom" / "test_c.py").write_text("# repo-local custom\n")
|
||||
(rl / "test_restore.py").write_text("# repo-local lifecycle overlay\n")
|
||||
|
||||
monkeypatch.setattr(discovery, "cc_ci_dir", lambda r: str(ccci_root / r))
|
||||
monkeypatch.setattr(meta_mod, "TESTS_DIR", str(ccci_root)) # compose.ccci.yml discovery
|
||||
monkeypatch.setattr(meta_mod, "TESTS_DIR", str(ccci_root))
|
||||
approved_file = tmp_path / "approved.txt"
|
||||
approved_file.write_text(f"{RECIPE}\n" if approved else "")
|
||||
monkeypatch.setenv("CCCI_REPO_LOCAL_APPROVED_FILE", str(approved_file))
|
||||
@ -61,7 +60,6 @@ def _mk_synthetic(tmp_path, monkeypatch, approved=True):
|
||||
|
||||
|
||||
def test_manifest_complete(tmp_path, monkeypatch):
|
||||
# Every surface the synthetic recipe customizes appears — nothing silently dropped (R4).
|
||||
meta, rl = _mk_synthetic(tmp_path, monkeypatch)
|
||||
m = manifest.build(RECIPE, meta, rl)
|
||||
assert m["meta_non_default"] == {
|
||||
@ -76,8 +74,8 @@ def test_manifest_complete(tmp_path, monkeypatch):
|
||||
}
|
||||
assert m["overlays"] == {"backup": "cc-ci", "restore": "repo-local"}
|
||||
assert m["custom_tests"] == {
|
||||
"cc-ci": {"functional": 2, "playwright": 1},
|
||||
"repo-local": {"functional": 1},
|
||||
"cc-ci": {"custom": 3},
|
||||
"repo-local": {"custom": 1},
|
||||
}
|
||||
assert m["env_overrides"] == []
|
||||
|
||||
@ -87,11 +85,10 @@ def test_manifest_deterministic_and_serializable(tmp_path, monkeypatch):
|
||||
a = manifest.build(RECIPE, meta, rl)
|
||||
b = manifest.build(RECIPE, meta, rl)
|
||||
assert json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True)
|
||||
assert json.loads(json.dumps(a)) == a # round-trips: no callables/tuples leak through
|
||||
assert json.loads(json.dumps(a)) == a
|
||||
|
||||
|
||||
def test_manifest_zero_config_floor(tmp_path, monkeypatch):
|
||||
# A recipe with NO customization at all -> every section empty, render says so explicitly.
|
||||
ccci_root = tmp_path / "cc-ci-tests"
|
||||
(ccci_root / RECIPE).mkdir(parents=True)
|
||||
monkeypatch.setattr(discovery, "cc_ci_dir", lambda r: str(ccci_root / r))
|
||||
@ -112,31 +109,25 @@ def test_manifest_zero_config_floor(tmp_path, monkeypatch):
|
||||
|
||||
|
||||
def test_manifest_repo_local_hc2_gate(tmp_path, monkeypatch):
|
||||
# Unapproved recipe -> repo-local overlay + custom tests INVISIBLE (same default-deny as the
|
||||
# discovery they ride on; the manifest must not advertise code the run will not execute).
|
||||
meta, rl = _mk_synthetic(tmp_path, monkeypatch, approved=False)
|
||||
m = manifest.build(RECIPE, meta, rl)
|
||||
assert m["overlays"] == {"backup": "cc-ci"} # repo-local test_restore.py gone
|
||||
assert m["overlays"] == {"backup": "cc-ci"}
|
||||
assert "repo-local" not in m["custom_tests"]
|
||||
|
||||
|
||||
def test_manifest_env_overrides_and_ci_flag(tmp_path, monkeypatch):
|
||||
meta, rl = _mk_synthetic(tmp_path, monkeypatch)
|
||||
monkeypatch.setenv("CCCI_SKIP_GENERIC_BACKUP", "1")
|
||||
monkeypatch.setenv("CCCI_SKIP_GENERIC_UPGRADE", "0") # falsy -> not an active override
|
||||
monkeypatch.setenv("CCCI_SKIP_GENERIC_UPGRADE", "0")
|
||||
m = manifest.build(RECIPE, meta, rl)
|
||||
assert m["env_overrides"] == ["CCCI_SKIP_GENERIC_BACKUP"]
|
||||
monkeypatch.delenv("DRONE", raising=False)
|
||||
assert "!!" not in manifest.render(RECIPE, m) # local dev: no CI warning
|
||||
monkeypatch.setenv("DRONE", "true") # riding a CI run -> loud flag (P2c)
|
||||
assert "!!" not in manifest.render(RECIPE, m)
|
||||
monkeypatch.setenv("DRONE", "true")
|
||||
assert "!! dev-only override active in CI" in manifest.render(RECIPE, m)
|
||||
|
||||
|
||||
def test_manifest_redacts_sensitive_named_values(tmp_path, monkeypatch):
|
||||
# Meta values are repo-public by construction, but the manifest lands on the dashboard:
|
||||
# secret-NAMED entries (top-level or nested dict keys, e.g. plausible's
|
||||
# EXTRA_ENV["SECRET_KEY_BASE"] dummy) render as '<redacted>' — name shown, value masked.
|
||||
# Non-sensitive names (incl. KEYCLOAK_* — 'KEY' matches only as a word segment) pass through.
|
||||
ccci_root = tmp_path / "cc-ci-tests"
|
||||
d = ccci_root / RECIPE
|
||||
d.mkdir(parents=True)
|
||||
@ -159,7 +150,7 @@ def test_manifest_redacts_sensitive_named_values(tmp_path, monkeypatch):
|
||||
}
|
||||
out = manifest.render(RECIPE, m)
|
||||
assert "dummy-ci-constant" not in out and "also-dummy" not in out
|
||||
assert "SECRET_KEY_BASE" in out # the key NAME stays visible
|
||||
assert "SECRET_KEY_BASE" in out
|
||||
|
||||
|
||||
def test_render_lists_every_surface(tmp_path, monkeypatch):
|
||||
@ -173,5 +164,5 @@ def test_render_lists_every_surface(tmp_path, monkeypatch):
|
||||
in lines
|
||||
)
|
||||
assert "overlays: test_backup.py(cc-ci) test_restore.py(repo-local)" in lines
|
||||
assert "custom tests: functional/=2 playwright/=1 (cc-ci) functional/=1 (repo-local)" in lines
|
||||
assert "custom tests: custom/=3 (cc-ci) custom/=1 (repo-local)" in lines
|
||||
assert "env overrides: (none)" in lines
|
||||
|
||||
Reference in New Issue
Block a user