3b1181f39b
v1.14 NFR Refinement milestone complete. 20 execution phases (P1-P20) + 1 final (P21). All P1/P2 backlog from v1.11 review resolved. Security posture hardened (swallowed errors, account ID externalized, IAM scoped, schema validation, credential hygiene). Stubs resolved (kyverno --kube- version removed). 7 untested scripts gained coverage. Documentation synced (ARCHITECTURE v1.11-v1.14 addenda, stale @v1.6-1.9 -> @v1.13, GRILL G-005/G-008 resolved, COST.md window extended, D-083 deferral recorded). Platform VPC parameterized. 561 tests pass (was 528 at v1.13.2; +33). 22/22 capabilities Verified. 6 grill binding decisions (G-101..G-106) applied. 1 escalation (E-001) auto-resolved at full autonomy (D-101). ---ci--- project: acdl phase: 21 milestone: v1.14 status: complete ---/ci---
159 lines
5.2 KiB
Python
159 lines
5.2 KiB
Python
"""REQ-104: environment JSON schema + qa/prod/dr bindings + load()."""
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import jsonschema
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from core.environment_check import load, check
|
|
|
|
ENV_DIR = ROOT / "core" / "environments"
|
|
SCHEMA = ROOT / "schemas" / "environment.schema.json"
|
|
|
|
ENV_FILES = ["dev.json", "qa.json", "prod.json", "dr.json"]
|
|
|
|
|
|
def _schema():
|
|
return json.loads(SCHEMA.read_text())
|
|
|
|
|
|
@pytest.mark.parametrize("env_file", ENV_FILES)
|
|
def test_env_file_validates_against_schema(env_file):
|
|
env = json.loads((ENV_DIR / env_file).read_text())
|
|
jsonschema.validate(env, _schema())
|
|
|
|
|
|
def test_dev_env_has_expected_fields():
|
|
env = load("dev")
|
|
assert env["name"] == "dev"
|
|
assert env["account_id"] == "000000000000"
|
|
assert env["region"] == "us-east-1"
|
|
assert env["autonomy"] == "full"
|
|
assert env["confidence_threshold"] == 0.50
|
|
assert "state_backend" in env
|
|
assert "bucket" in env["state_backend"]
|
|
assert "network" in env
|
|
|
|
|
|
def test_qa_env_attested_with_075_threshold():
|
|
env = load("qa")
|
|
assert env["autonomy"] == "attested"
|
|
assert env["confidence_threshold"] == 0.75
|
|
|
|
|
|
def test_prod_env_attested_with_090_threshold():
|
|
env = load("prod")
|
|
assert env["autonomy"] == "attested"
|
|
assert env["confidence_threshold"] == 0.90
|
|
|
|
|
|
def test_dr_env_attested_with_095_threshold():
|
|
env = load("dr")
|
|
assert env["autonomy"] == "attested"
|
|
assert env["confidence_threshold"] == 0.95
|
|
|
|
|
|
def test_load_unknown_env_raises():
|
|
with pytest.raises(FileNotFoundError):
|
|
load("nonexistent")
|
|
|
|
|
|
def test_load_returns_dict():
|
|
env = load("dev")
|
|
assert isinstance(env, dict)
|
|
|
|
|
|
def test_placeholder_account_warning_for_non_dev(capsys):
|
|
"""A stderr warning is emitted when account_id is the placeholder and env != dev."""
|
|
load("qa")
|
|
captured = capsys.readouterr()
|
|
assert "placeholder account_id" in captured.err
|
|
assert "qa" in captured.err
|
|
|
|
|
|
def test_no_warning_for_dev_placeholder(capsys):
|
|
load("dev")
|
|
captured = capsys.readouterr()
|
|
assert "placeholder account_id" not in captured.err
|
|
|
|
|
|
def test_check_still_works_for_dev():
|
|
ok, msg = check(env_name="dev")
|
|
assert ok is True
|
|
|
|
|
|
def test_check_fails_for_unknown_env():
|
|
ok, msg = check(env_name="nonexistent")
|
|
assert ok is False
|
|
assert "nonexistent" in msg
|
|
|
|
|
|
def test_account_id_is_12_digits():
|
|
for env_file in ENV_FILES:
|
|
env = json.loads((ENV_DIR / env_file).read_text())
|
|
assert len(env["account_id"]) == 12
|
|
assert env["account_id"].isdigit()
|
|
|
|
|
|
def test_v14_schema_rejects_undocumented_fields():
|
|
"""v1.14 (REQ-145): additionalProperties: false rejects unknown fields."""
|
|
schema = json.loads(SCHEMA.read_text())
|
|
bad_env = {
|
|
"name": "dev",
|
|
"account_id": "123456789012",
|
|
"region": "us-east-1",
|
|
"state_backend": {"bucket": "test", "lock_table": "test"},
|
|
"network": {"vpc_cidr": "10.0.0.0/16", "azs": ["us-east-1a"]},
|
|
"runner_role_arn": "arn:aws:iam::123456789012:role/test",
|
|
"autonomy": "full",
|
|
"confidence_threshold": 0.5,
|
|
"rogue_field": "should be rejected"
|
|
}
|
|
with pytest.raises(jsonschema.ValidationError, match="Additional properties are not allowed"):
|
|
jsonschema.validate(bad_env, schema)
|
|
|
|
|
|
def test_v14_schema_validates_bucket_name_format():
|
|
"""v1.14 (REQ-145): state_backend.bucket must match S3 naming rules."""
|
|
schema = json.loads(SCHEMA.read_text())
|
|
bad_env = {
|
|
"name": "dev", "account_id": "123456789012", "region": "us-east-1",
|
|
"state_backend": {"bucket": "Invalid_Bucket!", "lock_table": "test"},
|
|
"network": {"vpc_cidr": "10.0.0.0/16", "azs": ["us-east-1a"]},
|
|
"runner_role_arn": "arn:aws:iam::123456789012:role/test",
|
|
"autonomy": "full", "confidence_threshold": 0.5
|
|
}
|
|
with pytest.raises(jsonschema.ValidationError, match="does not match"):
|
|
jsonschema.validate(bad_env, schema)
|
|
|
|
|
|
def test_v14_schema_validates_arn_format():
|
|
"""v1.14 (REQ-145): runner_role_arn must match ARN format."""
|
|
schema = json.loads(SCHEMA.read_text())
|
|
bad_env = {
|
|
"name": "dev", "account_id": "123456789012", "region": "us-east-1",
|
|
"state_backend": {"bucket": "test", "lock_table": "test"},
|
|
"network": {"vpc_cidr": "10.0.0.0/16", "azs": ["us-east-1a"]},
|
|
"runner_role_arn": "not-an-arn",
|
|
"autonomy": "full", "confidence_threshold": 0.5
|
|
}
|
|
with pytest.raises(jsonschema.ValidationError, match="does not match"):
|
|
jsonschema.validate(bad_env, schema)
|
|
|
|
|
|
def test_v14_schema_validates_cidr_format():
|
|
"""v1.14 (REQ-145): vpc_cidr must match CIDR format."""
|
|
schema = json.loads(SCHEMA.read_text())
|
|
bad_env = {
|
|
"name": "dev", "account_id": "123456789012", "region": "us-east-1",
|
|
"state_backend": {"bucket": "test", "lock_table": "test"},
|
|
"network": {"vpc_cidr": "not-a-cidr", "azs": ["us-east-1a"]},
|
|
"runner_role_arn": "arn:aws:iam::123456789012:role/test",
|
|
"autonomy": "full", "confidence_threshold": 0.5
|
|
}
|
|
with pytest.raises(jsonschema.ValidationError, match="does not match"):
|
|
jsonschema.validate(bad_env, schema) |