import pytest from flexoentity import FlexOID, FlexoEntity, EntityState def test_initial_state(sample_domain): assert sample_domain.state == EntityState.DRAFT assert sample_domain.flexo_id.version == 1 assert FlexoEntity.verify_integrity(sample_domain) def test_approval_does_not_bump_version(sample_domain): q = sample_domain q.approve() assert q.state == EntityState.APPROVED assert q.flexo_id.version == 1 def test_signing_does_not_bump_version(sample_domain): q = sample_domain q.approve() before = q.flexo_id q.sign() after = q.flexo_id # state changed assert q.state == EntityState.APPROVED_AND_SIGNED # version unchanged assert before.version == after.version # only suffix letter differs assert before.prefix == after.prefix assert before.state_code == "A" assert after.state_code == "S" def test_publish_does_not_bump_version(sample_domain): q = sample_domain q.approve() q.sign() v_before = q.flexo_id.version q.publish() assert q.state == EntityState.PUBLISHED assert q.flexo_id.version == v_before def test_modify_content_changes_fingerprint(sample_signature): sample_signature.comment += "Rephrased content" # simulate text change changed = sample_signature._update_fingerprint() assert changed def test_no_version_bump_on_draft_edits(sample_signature): sample_signature.comment = "Minor draft edit" sample_signature._update_fingerprint() assert sample_signature.flexo_id.version == 1 def test_version_bump_after_edit_and_sign(sample_signature): sample_signature.approve() v1 = str(sample_signature.flexo_id) sample_signature.comment = "Changed comment" sample_signature.sign() assert str(sample_signature.flexo_id) != v1 def test_integrity_check_passes_and_fails(sample_signature): sample_signature.approve() assert FlexoEntity.verify_integrity(sample_signature) # simulate tampering sample_signature.comment = "Tampered text" assert not FlexoEntity.verify_integrity(sample_signature) def test_obsolete_state(sample_domain): q = sample_domain q.approve() q.sign() q.publish() q.obsolete() assert q.state == EntityState.OBSOLETE def test_clone_new_base_resets_lineage(sample_domain): q = sample_domain q.approve() q.sign() q.publish() q.obsolete() old_id = str(q.flexo_id) q.clone_new_base() assert str(q.flexo_id) != old_id assert q.state == EntityState.DRAFT assert q.flexo_id.version == 1 def test_clone_new_base_sets_origin(sample_domain): q = sample_domain q.approve() q.sign() q.publish() q.obsolete() old_id = str(q.flexo_id) q.clone_new_base() assert q.origin == old_id assert q.state == EntityState.DRAFT assert q.flexo_id.version == 1 assert q.flexo_id != old_id def test_mass_version_increments_until_obsolete(sample_domain): q = sample_domain q.approve() for _ in range(FlexOID.MAX_VERSION - 1): q.bump_version() # Next one must raise with pytest.raises(RuntimeError, match="mark obsolete"): q.bump_version() def test_fork_creates_independent_domain(sample_domain): original = sample_domain forked = original.fork(domain_id="DST") # ─── identity ─────────────────────────────────────────────────────── assert forked is not original assert isinstance(forked.flexo_id, FlexOID) assert forked.flexo_id != original.flexo_id # ─── lifecycle reset ──────────────────────────────────────────────── assert forked.state == EntityState.DRAFT assert forked.version == 1 # ─── provenance ───────────────────────────────────────────────────── assert forked.origin == str(original.flexo_id) # ─── domain reassigned ────────────────────────────────────────────── assert forked.domain_id == "DST" def test_fork_does_not_mutate_original(sample_domain): original = sample_domain original_id = original.flexo_id original_fp = original.fingerprint _ = original.fork(domain_id="DST") assert original.flexo_id == original_id assert original.fingerprint == original_fp assert original.origin is None def test_fork_does_not_shadow_state(sample_domain): forked = sample_domain.fork(domain_id="DST") # No instance attribute allowed assert "state" not in forked.__dict__ # Property must still work assert forked.state == EntityState.DRAFT def test_fork_without_origin(sample_domain): forked = sample_domain.fork(domain_id="DST", keep_origin=False) assert forked.origin is None