generated from coulomb/repo-seed
Versioning and restoration of assets
This commit is contained in:
@@ -149,6 +149,124 @@ def test_asset_registry_create_is_idempotent_for_same_key_and_payload() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_asset_registry_rejects_stale_expected_current_version_for_mutations() -> None:
|
||||
repo = InMemoryAssetRegistryRepository()
|
||||
service = AssetRegistryService(repo)
|
||||
context = operation_context()
|
||||
created = service.create_asset(
|
||||
"Conflict Guard",
|
||||
Classification(asset_type="document", sensitivity=Sensitivity.INTERNAL),
|
||||
context,
|
||||
asset_id="asset-conflict",
|
||||
)
|
||||
updated = service.add_metadata_record(
|
||||
created.asset.id,
|
||||
MetadataRecord("owner", "Platform Knowledge", confirmed=True),
|
||||
context,
|
||||
expected_current_version_id=created.version.version_id,
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationError) as exc_info:
|
||||
service.transition_lifecycle(
|
||||
created.asset.id,
|
||||
LifecycleState.RETIRED,
|
||||
context,
|
||||
expected_current_version_id=created.version.version_id,
|
||||
)
|
||||
|
||||
assert exc_info.value.details["code"] == "asset.version_conflict"
|
||||
assert exc_info.value.details["expected_current_version_id"] == created.version.version_id
|
||||
assert exc_info.value.details["current_version_id"] == updated.version.version_id
|
||||
assert repo.get_asset(created.asset.id).lifecycle == LifecycleState.ACTIVE
|
||||
assert [version.sequence for version in repo.list_versions(created.asset.id)] == [1, 2]
|
||||
|
||||
|
||||
def test_asset_registry_restore_creates_new_version_without_erasing_history() -> None:
|
||||
repo = InMemoryAssetRegistryRepository()
|
||||
service = AssetRegistryService(repo)
|
||||
context = operation_context()
|
||||
created = service.create_asset(
|
||||
"Restorable Asset",
|
||||
Classification(asset_type="document", sensitivity=Sensitivity.INTERNAL),
|
||||
context,
|
||||
asset_id="asset-restorable",
|
||||
)
|
||||
retired = service.transition_lifecycle(
|
||||
created.asset.id,
|
||||
LifecycleState.RETIRED,
|
||||
context,
|
||||
expected_current_version_id=created.version.version_id,
|
||||
)
|
||||
|
||||
restored = service.restore_asset_version(
|
||||
created.asset.id,
|
||||
created.version.version_id,
|
||||
context,
|
||||
expected_current_version_id=retired.version.version_id,
|
||||
)
|
||||
|
||||
versions = repo.list_versions(created.asset.id)
|
||||
|
||||
assert restored.version.change_type.value == "restored"
|
||||
assert restored.version.parent_version_id == retired.version.version_id
|
||||
assert restored.version.metadata_delta["restored_from_version_id"] == created.version.version_id
|
||||
assert restored.version.metadata_delta["restored_from_sequence"] == 1
|
||||
assert restored.asset.lifecycle == LifecycleState.ACTIVE
|
||||
assert repo.get_asset(created.asset.id).current_version_id == restored.version.version_id
|
||||
assert [version.sequence for version in versions] == [1, 2, 3]
|
||||
assert [version.change_type.value for version in versions] == [
|
||||
"created",
|
||||
"lifecycle_changed",
|
||||
"restored",
|
||||
]
|
||||
assert [event.operation for event in repo.list_audit_events(target="asset:asset-restorable")] == [
|
||||
"asset.create",
|
||||
"asset.lifecycle.transition",
|
||||
"asset.version.restore",
|
||||
]
|
||||
|
||||
|
||||
def test_asset_registry_supersede_creates_relationship_version_and_audit() -> None:
|
||||
repo = InMemoryAssetRegistryRepository()
|
||||
service = AssetRegistryService(repo)
|
||||
context = operation_context()
|
||||
source = service.create_asset(
|
||||
"Old Guide",
|
||||
Classification(asset_type="guide", sensitivity=Sensitivity.INTERNAL),
|
||||
context,
|
||||
asset_id="asset-old-guide",
|
||||
)
|
||||
successor = service.create_asset(
|
||||
"New Guide",
|
||||
Classification(asset_type="guide", sensitivity=Sensitivity.INTERNAL),
|
||||
context,
|
||||
asset_id="asset-new-guide",
|
||||
)
|
||||
|
||||
superseded = service.supersede_asset(
|
||||
source.asset.id,
|
||||
successor.asset.id,
|
||||
context,
|
||||
reason="New canonical guide",
|
||||
expected_current_version_id=source.version.version_id,
|
||||
)
|
||||
|
||||
source_asset = repo.get_asset(source.asset.id)
|
||||
relationships = repo.list_relationships(source_id=source.asset.id)
|
||||
|
||||
assert superseded.version.change_type.value == "superseded"
|
||||
assert superseded.relationship.predicate == "superseded_by"
|
||||
assert superseded.relationship.target_id == successor.asset.id
|
||||
assert relationships == [superseded.relationship]
|
||||
assert source_asset.lifecycle == LifecycleState.RETIRED
|
||||
assert source_asset.metadata["superseded_by"] == successor.asset.id
|
||||
assert source_asset.metadata["supersession_reason"] == "New canonical guide"
|
||||
assert superseded.version.relationship_delta["added"]["relationship_id"] == superseded.relationship.relationship_id
|
||||
assert superseded.version.metadata_delta["superseded_by"] == successor.asset.id
|
||||
assert repo.get_asset(successor.asset.id).current_version_id == successor.version.version_id
|
||||
assert repo.list_audit_events(target="asset:asset-old-guide")[-1].operation == "asset.supersede"
|
||||
|
||||
|
||||
def test_asset_registry_relationships_create_versions_and_audit() -> None:
|
||||
repo = InMemoryAssetRegistryRepository()
|
||||
service = AssetRegistryService(repo)
|
||||
|
||||
Reference in New Issue
Block a user