This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
qqq-orb/tests/test_validate_standard.py
james.maesandOpenAI Codex 64e8d68cb3 fix: preserve QBit publication policies during fleet migration
Keep audited main-push release paths, bounded tag policies and test exclusions. Reject unknown filters and retain 630 original-to-generated publication comparisons across all 42 QBits.

Co-Authored-By: OpenAI Codex <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-20 06:53:01 -05:00

136 lines
6.0 KiB
Python

import copy
import importlib.util
from pathlib import Path
import unittest
import yaml
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location("validate_standard", ROOT / "src/scripts/validate_standard.py")
standard = importlib.util.module_from_spec(spec)
spec.loader.exec_module(standard)
VERSION = "0.7.0"
CALLER = {
"version": 2.1, "setup": True,
"orbs": {"qqq-orb": "kingsrook/qqq-orb@0.7.0"},
"workflows": {"setup": {"jobs": [{"qqq-orb/generate_pipeline": {
"orb_version": "0.7.0",
"filters": {"branches": {"ignore": "automation/qbit-rc-pending"}, "tags": {"only": "/.*/"}},
}}]}},
}
class ValidateStandard(unittest.TestCase):
def test_declarative_qbit_tag_filters_match_migration_and_manifest(self):
spec = importlib.util.spec_from_file_location("migration_policy", ROOT / "tools/standardize_qbit.py")
migration = importlib.util.module_from_spec(spec)
spec.loader.exec_module(migration)
for policy in ("stable", "v-prefix", "none"):
caller = migration.canonical_caller(VERSION, policy)
manifest = {"profile": "qbit", "orb_version": VERSION, "default_branch": "main",
"release_tag_policy": policy}
standard.validate(yaml.safe_dump(caller), yaml.safe_dump(manifest), VERSION)
caller["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]["filters"]["tags"] = {"only": "/custom.*/"}
with self.assertRaises(standard.StandardError):
standard.validate(yaml.safe_dump(caller), yaml.safe_dump(manifest), VERSION)
def check(self, config, profile="qbit", version=VERSION):
standard.validate(yaml.safe_dump(config), yaml.safe_dump({"profile": profile, "orb_version": version, "default_branch": "develop"}), VERSION)
def rejected(self, config, profile="qbit"):
with self.assertRaises(standard.StandardError):
self.check(config, profile)
def test_standardizer_normal_caller_matches(self):
self.check(CALLER)
spec = importlib.util.spec_from_file_location("standardize_qbit", ROOT / "tools/standardize_qbit.py")
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self.assertEqual(module.canonical_caller(VERSION), CALLER)
def test_all_profile_parameter_contracts(self):
for profile in standard.PROFILE_PARAMETERS:
with self.subTest(profile=profile):
self.check(standard.canonical_caller(profile, VERSION), profile)
def test_custom_jobs_and_commands_rejected(self):
for key in ("jobs", "commands"):
config = copy.deepcopy(CALLER)
config[key] = {"custom": {"steps": ["checkout"]}}
self.rejected(config)
def test_extra_workflow_and_second_job_rejected(self):
config = copy.deepcopy(CALLER)
config["workflows"]["custom"] = {"jobs": []}
self.rejected(config)
config = copy.deepcopy(CALLER)
config["workflows"]["setup"]["jobs"].append("custom")
self.rejected(config)
def test_import_job_and_manifest_version_mismatch(self):
config = copy.deepcopy(CALLER)
config["orbs"]["qqq-orb"] = "kingsrook/qqq-orb@0.6.9"
self.rejected(config)
config = copy.deepcopy(CALLER)
config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]["orb_version"] = "0.6.9"
self.rejected(config)
with self.assertRaises(standard.StandardError):
self.check(CALLER, version="0.6.9")
def test_extra_orb_rejected(self):
config = copy.deepcopy(CALLER)
config["orbs"]["other"] = "circleci/node@1.0.0"
self.rejected(config)
def test_unknown_pipeline_and_job_parameters_rejected(self):
config = copy.deepcopy(CALLER)
config["parameters"] = {"custom": {"type": "string", "default": ""}}
self.rejected(config)
config = copy.deepcopy(CALLER)
config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]["custom"] = True
self.rejected(config)
def test_profile_parameters_required_and_exactly_forwarded(self):
self.rejected(CALLER, "core")
config = standard.canonical_caller("core", VERSION)
config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]["mongo_bind_diagnostics"] = False
self.rejected(config, "core")
config = standard.canonical_caller("frontend-material", VERSION)
self.rejected(config, "frontend-core")
def test_duplicate_yaml_keys_rejected(self):
config = yaml.safe_dump(CALLER)
manifest = "profile: qbit\norb_version: 0.7.0\n"
for candidate, candidate_manifest in ((config + "setup: true\n", manifest), (config, manifest + "profile: cli\n"), (config.replace("orb_version: 0.7.0", "orb_version: 0.7.0\n orb_version: 0.7.0"), manifest)):
with self.subTest(candidate=candidate):
with self.assertRaises(standard.StandardError):
standard.validate(candidate, candidate_manifest, VERSION)
def test_wrong_boolean_type_rejected(self):
config = copy.deepcopy(CALLER)
config["setup"] = 1
self.rejected(config)
def test_exact_filters_required(self):
config = copy.deepcopy(CALLER)
config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]["filters"]["branches"]["ignore"] = "main"
self.rejected(config)
def test_only_standard_manifest_path_allowed(self):
config = copy.deepcopy(CALLER)
options = config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"]
options["manifest"] = ".qqq-ci.yml"
self.check(config)
options["manifest"] = "custom.yml"
self.rejected(config)
def test_setup_context_or_presteps_rejected(self):
for key, value in (("context", "custom"), ("pre-steps", ["checkout"])):
config = copy.deepcopy(CALLER)
config["workflows"]["setup"]["jobs"][0]["qqq-orb/generate_pipeline"][key] = value
self.rejected(config)
if __name__ == "__main__":
unittest.main()