|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | import json |
| 6 | +from dataclasses import replace |
6 | 7 | from pathlib import Path |
7 | 8 |
|
8 | 9 | import pytest |
|
13 | 14 | PARTICIPANT_RUNTIME_ROLE_SCOPE, |
14 | 15 | BackendManifest, |
15 | 16 | OrchestratorCapabilities, |
| 17 | + ParticipantFeatureSupport, |
16 | 18 | ParticipantRuntimeCapabilities, |
17 | 19 | ProvisionerCapabilities, |
18 | 20 | participant_runtime_capability_contract_gaps, |
@@ -125,6 +127,7 @@ def test_backend_manifest_v2_declares_participant_capability_dimensions(): |
125 | 127 | "interference", |
126 | 128 | "shared_state_change", |
127 | 129 | ] |
| 130 | + assert participant_runtime["feature_support"] == [] |
128 | 131 |
|
129 | 132 | model = BackendManifestV2Model.model_validate(payload) |
130 | 133 | assert model.capabilities.participant_runtime is not None |
@@ -189,6 +192,89 @@ def test_participant_runtime_capabilities_validate_api_405_vocabularies(): |
189 | 192 | ) |
190 | 193 |
|
191 | 194 |
|
| 195 | +def test_participant_feature_support_validates_api_407_declarations(): |
| 196 | + declaration = ParticipantFeatureSupport( |
| 197 | + feature="coordination", |
| 198 | + support_level=ParticipantFeatureSupportLevel.EXACT, |
| 199 | + ) |
| 200 | + |
| 201 | + assert declaration.support_level == ParticipantFeatureSupportLevel.EXACT |
| 202 | + |
| 203 | + with pytest.raises(ValueError, match="governed participant behavior or interaction feature"): |
| 204 | + ParticipantFeatureSupport( |
| 205 | + feature="custom_feature", |
| 206 | + support_level=ParticipantFeatureSupportLevel.EXACT, |
| 207 | + ) |
| 208 | + |
| 209 | + with pytest.raises(ValueError, match="disclosure_refs"): |
| 210 | + ParticipantFeatureSupport( |
| 211 | + feature="coordination", |
| 212 | + support_level=ParticipantFeatureSupportLevel.BOUNDED, |
| 213 | + ) |
| 214 | + |
| 215 | + unsupported_declaration = ParticipantFeatureSupport( |
| 216 | + feature="coordination", |
| 217 | + support_level=ParticipantFeatureSupportLevel.UNSUPPORTED, |
| 218 | + disclosure_refs=("disclosures.coordination.unsupported.v1",), |
| 219 | + ) |
| 220 | + with pytest.raises(ValueError, match="supported feature unsupported"): |
| 221 | + ParticipantRuntimeCapabilities( |
| 222 | + name="participant-runtime", |
| 223 | + supported_participant_roles=frozenset({"blue"}), |
| 224 | + supported_behavior_features=frozenset({"action_contracts"}), |
| 225 | + supported_interaction_features=frozenset({"coordination"}), |
| 226 | + feature_support=(unsupported_declaration,), |
| 227 | + ) |
| 228 | + |
| 229 | + |
| 230 | +def test_backend_manifest_payload_renders_api_407_feature_support_entries(): |
| 231 | + manifest = create_stub_manifest() |
| 232 | + assert manifest.participant_runtime is not None |
| 233 | + participant_runtime = replace( |
| 234 | + manifest.participant_runtime, |
| 235 | + feature_support=( |
| 236 | + ParticipantFeatureSupport( |
| 237 | + feature="behavior_history", |
| 238 | + support_level=ParticipantFeatureSupportLevel.BOUNDED, |
| 239 | + constraint_refs=("constraints.behavior-history.retention-window",), |
| 240 | + disclosure_refs=("disclosures.behavior-history.bounded.v1",), |
| 241 | + ), |
| 242 | + ParticipantFeatureSupport( |
| 243 | + feature="coordination", |
| 244 | + support_level=ParticipantFeatureSupportLevel.EXACT, |
| 245 | + ), |
| 246 | + ), |
| 247 | + ) |
| 248 | + capabilities = replace(manifest.capabilities, participant_runtime=participant_runtime) |
| 249 | + manifest = BackendManifest( |
| 250 | + identity=manifest.identity, |
| 251 | + supported_contract_versions=manifest.supported_contract_versions, |
| 252 | + compatibility=manifest.compatibility, |
| 253 | + realization_support=manifest.realization_support, |
| 254 | + concept_bindings=manifest.concept_bindings, |
| 255 | + constraints=manifest.constraints, |
| 256 | + capabilities=capabilities, |
| 257 | + ) |
| 258 | + |
| 259 | + payload = backend_manifest_payload(manifest) |
| 260 | + |
| 261 | + assert payload["capabilities"]["participant_runtime"]["feature_support"] == [ |
| 262 | + { |
| 263 | + "feature": "behavior_history", |
| 264 | + "support_level": "bounded", |
| 265 | + "constraint_refs": ["constraints.behavior-history.retention-window"], |
| 266 | + "disclosure_refs": ["disclosures.behavior-history.bounded.v1"], |
| 267 | + }, |
| 268 | + { |
| 269 | + "feature": "coordination", |
| 270 | + "support_level": "exact", |
| 271 | + "constraint_refs": [], |
| 272 | + "disclosure_refs": [], |
| 273 | + }, |
| 274 | + ] |
| 275 | + BackendManifestV2Model.model_validate(payload) |
| 276 | + |
| 277 | + |
192 | 278 | def test_participant_runtime_capability_evidence_covers_standard_vocabularies(): |
193 | 279 | catalog_path = FIXTURES_ROOT / "concept-authority" / "controlled-vocabularies-v1" / "valid" / "reference.json" |
194 | 280 | catalog = json.loads(catalog_path.read_text(encoding="utf-8")) |
|
0 commit comments