import pytest from fan_passport.admin_content import ( AdminActor, AdminContentService, ContentChange, ContentChangeSet, ContentEntityType, ContentOperationType, ContentValidationError, InMemoryContentRepository, ) def admin_actor(*permissions: str) -> AdminActor: return AdminActor( actor_id="admin-1", display_name="Admin One", role="admin", permissions=frozenset(permissions), ) def change_set(changes, *, actor=None, dry_run=False) -> ContentChangeSet: return ContentChangeSet( change_set_id="change-set-1", actor=actor or admin_actor("content:write", "content:delete"), reason="Correct tournament content", changes=tuple(changes), dry_run=dry_run, ) def test_plan_accepts_match_referencing_entities_created_in_same_change_set() -> None: repository = InMemoryContentRepository() service = AdminContentService(repository) plan = service.plan( change_set( [ ContentChange( entity_type=ContentEntityType.TEAM, operation=ContentOperationType.UPSERT, stable_id="ENG", payload={"name": "England"}, ), ContentChange( entity_type=ContentEntityType.TEAM, operation=ContentOperationType.UPSERT, stable_id="USA", payload={"name": "United States"}, ), ContentChange( entity_type=ContentEntityType.MATCH, operation=ContentOperationType.UPSERT, stable_id="match-001", payload={ "home_team_id": "ENG", "away_team_id": "USA", "kickoff_at": "2026-06-11T19:00:00Z", }, ), ] ) ) assert plan.accepted assert plan.errors == () def test_apply_persists_revisions_and_audit_event() -> None: repository = InMemoryContentRepository() service = AdminContentService(repository) result = service.apply( change_set( [ ContentChange( entity_type="team", operation="upsert", stable_id="ENG", payload={"name": "England"}, ) ] ) ) assert result.applied is True assert result.revisions == {"team:ENG": 1} assert repository.get_payload(ContentEntityType.TEAM, "ENG") == {"name": "England"} assert repository.audit_event(result.audit_id)["applied"] is True def test_dry_run_records_audit_but_does_not_mutate_content() -> None: repository = InMemoryContentRepository() service = AdminContentService(repository) result = service.apply( change_set( [ ContentChange( entity_type="team", operation="upsert", stable_id="ENG", payload={"name": "England"}, ) ], dry_run=True, ) ) assert result.applied is False assert result.revisions == {} assert repository.get_payload("team", "ENG") is None assert repository.audit_event(result.audit_id)["applied"] is False def test_missing_permission_rejects_change_set() -> None: repository = InMemoryContentRepository() service = AdminContentService(repository) plan = service.plan( change_set( [ ContentChange( entity_type="team", operation="upsert", stable_id="ENG", payload={"name": "England"}, ) ], actor=admin_actor(), ) ) assert not plan.accepted assert any(issue.code == "missing_write_permission" for issue in plan.errors) def test_revision_conflict_blocks_apply() -> None: repository = InMemoryContentRepository() repository.seed(ContentEntityType.TEAM, "ENG", {"name": "England"}, revision=3) service = AdminContentService(repository) with pytest.raises(ContentValidationError, match="revision_conflict"): service.apply( change_set( [ ContentChange( entity_type="team", operation="upsert", stable_id="ENG", payload={"name": "England Men"}, expected_revision=2, ) ] ) )