|
| 1 | +"""Tests for EBM- evidence bundle manifest schema.""" |
| 2 | + |
| 3 | +import pytest |
| 4 | +from openamp_foundry.evidence.evidence_bundle_manifest import ( |
| 5 | + EvidenceBundleManifest, |
| 6 | + ManifestEntry, |
| 7 | + VALID_MANIFEST_SCHEMA_TYPES, |
| 8 | + VALID_MANIFEST_STATUSES, |
| 9 | + build_evidence_bundle_manifest, |
| 10 | + format_evidence_bundle_manifest, |
| 11 | + validate_evidence_bundle_manifest, |
| 12 | +) |
| 13 | + |
| 14 | +# --------------------------------------------------------------------------- |
| 15 | +# Helpers |
| 16 | +# --------------------------------------------------------------------------- |
| 17 | + |
| 18 | +_TWO_ENTRIES = [ |
| 19 | + {"schema_type": "BSP", "artifact_id": "A1", "description": "batch selection"}, |
| 20 | + {"schema_type": "PCC", "artifact_id": "A2", "description": "completeness cert"}, |
| 21 | +] |
| 22 | + |
| 23 | + |
| 24 | +def _build(**kwargs): |
| 25 | + defaults = dict( |
| 26 | + ebm_id="EBM-001", |
| 27 | + batch_id="BATCH-01", |
| 28 | + pipeline_version="v1.0", |
| 29 | + entry_dicts=_TWO_ENTRIES, |
| 30 | + limitations=["dry-lab only"], |
| 31 | + created_at="2026-07-10", |
| 32 | + ) |
| 33 | + defaults.update(kwargs) |
| 34 | + return build_evidence_bundle_manifest(**defaults) |
| 35 | + |
| 36 | + |
| 37 | +# --------------------------------------------------------------------------- |
| 38 | +# 1. Constants |
| 39 | +# --------------------------------------------------------------------------- |
| 40 | + |
| 41 | + |
| 42 | +def test_valid_manifest_schema_types_is_frozenset(): |
| 43 | + assert isinstance(VALID_MANIFEST_SCHEMA_TYPES, frozenset) |
| 44 | + |
| 45 | + |
| 46 | +def test_valid_manifest_schema_types_contains_bsp(): |
| 47 | + assert "BSP" in VALID_MANIFEST_SCHEMA_TYPES |
| 48 | + |
| 49 | + |
| 50 | +def test_valid_manifest_schema_types_contains_pcc(): |
| 51 | + assert "PCC" in VALID_MANIFEST_SCHEMA_TYPES |
| 52 | + |
| 53 | + |
| 54 | +def test_valid_manifest_schema_types_contains_prg(): |
| 55 | + assert "PRG" in VALID_MANIFEST_SCHEMA_TYPES |
| 56 | + |
| 57 | + |
| 58 | +def test_valid_manifest_schema_types_contains_eci(): |
| 59 | + assert "ECI" in VALID_MANIFEST_SCHEMA_TYPES |
| 60 | + |
| 61 | + |
| 62 | +def test_valid_manifest_schema_types_count(): |
| 63 | + assert len(VALID_MANIFEST_SCHEMA_TYPES) == 19 |
| 64 | + |
| 65 | + |
| 66 | +def test_valid_manifest_statuses_is_frozenset(): |
| 67 | + assert isinstance(VALID_MANIFEST_STATUSES, frozenset) |
| 68 | + |
| 69 | + |
| 70 | +def test_valid_manifest_statuses_contains_complete(): |
| 71 | + assert "complete" in VALID_MANIFEST_STATUSES |
| 72 | + |
| 73 | + |
| 74 | +def test_valid_manifest_statuses_contains_partial(): |
| 75 | + assert "partial" in VALID_MANIFEST_STATUSES |
| 76 | + |
| 77 | + |
| 78 | +def test_valid_manifest_statuses_contains_empty(): |
| 79 | + assert "empty" in VALID_MANIFEST_STATUSES |
| 80 | + |
| 81 | + |
| 82 | +# --------------------------------------------------------------------------- |
| 83 | +# 2. build – happy paths |
| 84 | +# --------------------------------------------------------------------------- |
| 85 | + |
| 86 | + |
| 87 | +def test_build_returns_evidence_bundle_manifest(): |
| 88 | + assert isinstance(_build(), EvidenceBundleManifest) |
| 89 | + |
| 90 | + |
| 91 | +def test_build_ebm_id_stored(): |
| 92 | + assert _build().ebm_id == "EBM-001" |
| 93 | + |
| 94 | + |
| 95 | +def test_build_batch_id_stored(): |
| 96 | + assert _build().batch_id == "BATCH-01" |
| 97 | + |
| 98 | + |
| 99 | +def test_build_pipeline_version_stored(): |
| 100 | + assert _build().pipeline_version == "v1.0" |
| 101 | + |
| 102 | + |
| 103 | +def test_build_dry_lab_only_true(): |
| 104 | + assert _build().dry_lab_only is True |
| 105 | + |
| 106 | + |
| 107 | +def test_build_n_entries_matches_input(): |
| 108 | + assert _build().n_entries == 2 |
| 109 | + |
| 110 | + |
| 111 | +def test_build_partial_status_when_subset(): |
| 112 | + assert _build().manifest_status == "partial" |
| 113 | + |
| 114 | + |
| 115 | +def test_build_empty_status_when_no_entries(): |
| 116 | + r = _build(entry_dicts=[]) |
| 117 | + assert r.manifest_status == "empty" |
| 118 | + |
| 119 | + |
| 120 | +def test_build_schema_types_included_sorted(): |
| 121 | + r = _build() |
| 122 | + assert r.schema_types_included == sorted(r.schema_types_included) |
| 123 | + |
| 124 | + |
| 125 | +def test_build_schema_types_deduplicates(): |
| 126 | + entries = [ |
| 127 | + {"schema_type": "BSP", "artifact_id": "A1", "description": "d1"}, |
| 128 | + {"schema_type": "BSP", "artifact_id": "A2", "description": "d2"}, |
| 129 | + ] |
| 130 | + r = _build(entry_dicts=entries) |
| 131 | + assert r.schema_types_included == ["BSP"] |
| 132 | + assert r.n_entries == 2 |
| 133 | + |
| 134 | + |
| 135 | +def test_build_entries_are_manifest_entry(): |
| 136 | + for e in _build().entries: |
| 137 | + assert isinstance(e, ManifestEntry) |
| 138 | + |
| 139 | + |
| 140 | +def test_build_entry_artifact_id_stored(): |
| 141 | + r = _build() |
| 142 | + assert r.entries[0].artifact_id == "A1" |
| 143 | + |
| 144 | + |
| 145 | +def test_build_entry_description_stored(): |
| 146 | + r = _build() |
| 147 | + assert r.entries[0].description == "batch selection" |
| 148 | + |
| 149 | + |
| 150 | +def test_build_empty_description_defaults_to_empty_string(): |
| 151 | + entries = [{"schema_type": "BSP", "artifact_id": "A1"}] |
| 152 | + r = _build(entry_dicts=entries) |
| 153 | + assert r.entries[0].description == "" |
| 154 | + |
| 155 | + |
| 156 | +def test_build_limitations_stored(): |
| 157 | + assert _build().limitations == ["dry-lab only"] |
| 158 | + |
| 159 | + |
| 160 | +def test_build_created_at_stored(): |
| 161 | + assert _build().created_at == "2026-07-10" |
| 162 | + |
| 163 | + |
| 164 | +def test_build_complete_status_when_all_types_present(): |
| 165 | + all_entries = [ |
| 166 | + {"schema_type": t, "artifact_id": f"ART-{t}", "description": ""} |
| 167 | + for t in VALID_MANIFEST_SCHEMA_TYPES |
| 168 | + ] |
| 169 | + r = _build(entry_dicts=all_entries) |
| 170 | + assert r.manifest_status == "complete" |
| 171 | + |
| 172 | + |
| 173 | +# --------------------------------------------------------------------------- |
| 174 | +# 3. validate – rejection cases |
| 175 | +# --------------------------------------------------------------------------- |
| 176 | + |
| 177 | + |
| 178 | +def test_validate_rejects_bad_ebm_id_prefix(): |
| 179 | + with pytest.raises(ValueError, match="EBM-"): |
| 180 | + _build(ebm_id="BAD-001") |
| 181 | + |
| 182 | + |
| 183 | +def test_validate_rejects_empty_batch_id(): |
| 184 | + with pytest.raises(ValueError): |
| 185 | + _build(batch_id="") |
| 186 | + |
| 187 | + |
| 188 | +def test_validate_rejects_empty_pipeline_version(): |
| 189 | + with pytest.raises(ValueError): |
| 190 | + _build(pipeline_version="") |
| 191 | + |
| 192 | + |
| 193 | +def test_validate_rejects_unknown_schema_type(): |
| 194 | + entries = [{"schema_type": "UNKNOWN", "artifact_id": "A1", "description": "x"}] |
| 195 | + with pytest.raises(ValueError, match="VALID_MANIFEST_SCHEMA_TYPES"): |
| 196 | + _build(entry_dicts=entries) |
| 197 | + |
| 198 | + |
| 199 | +def test_validate_rejects_empty_artifact_id(): |
| 200 | + entries = [{"schema_type": "BSP", "artifact_id": "", "description": "x"}] |
| 201 | + with pytest.raises(ValueError, match="artifact_id"): |
| 202 | + _build(entry_dicts=entries) |
| 203 | + |
| 204 | + |
| 205 | +def test_validate_rejects_empty_limitations(): |
| 206 | + with pytest.raises(ValueError, match="limitations"): |
| 207 | + _build(limitations=[]) |
| 208 | + |
| 209 | + |
| 210 | +def test_validate_rejects_empty_created_at(): |
| 211 | + with pytest.raises(ValueError): |
| 212 | + _build(created_at="") |
| 213 | + |
| 214 | + |
| 215 | +# --------------------------------------------------------------------------- |
| 216 | +# 4. format |
| 217 | +# --------------------------------------------------------------------------- |
| 218 | + |
| 219 | + |
| 220 | +def test_format_contains_ebm_id(): |
| 221 | + assert "EBM-001" in format_evidence_bundle_manifest(_build()) |
| 222 | + |
| 223 | + |
| 224 | +def test_format_contains_batch_id(): |
| 225 | + assert "BATCH-01" in format_evidence_bundle_manifest(_build()) |
| 226 | + |
| 227 | + |
| 228 | +def test_format_contains_status(): |
| 229 | + assert "partial" in format_evidence_bundle_manifest(_build()) |
| 230 | + |
| 231 | + |
| 232 | +def test_format_contains_schema_types(): |
| 233 | + formatted = format_evidence_bundle_manifest(_build()) |
| 234 | + assert "BSP" in formatted |
| 235 | + assert "PCC" in formatted |
| 236 | + |
| 237 | + |
| 238 | +def test_format_contains_artifact_ids(): |
| 239 | + assert "A1" in format_evidence_bundle_manifest(_build()) |
| 240 | + |
| 241 | + |
| 242 | +def test_format_contains_description(): |
| 243 | + assert "batch selection" in format_evidence_bundle_manifest(_build()) |
| 244 | + |
| 245 | + |
| 246 | +def test_format_contains_limitations(): |
| 247 | + assert "dry-lab only" in format_evidence_bundle_manifest(_build()) |
| 248 | + |
| 249 | + |
| 250 | +def test_format_contains_dry_lab_only(): |
| 251 | + assert "dry_lab_only: True" in format_evidence_bundle_manifest(_build()) |
| 252 | + |
| 253 | + |
| 254 | +def test_format_is_string(): |
| 255 | + assert isinstance(format_evidence_bundle_manifest(_build()), str) |
0 commit comments