"""Tests for the page model + capability profile (SHARD-WP-0007 T2).""" import pytest from shard_wiki.model import ( AccessGrant, Addressing, AttachmentMode, CapabilityProfile, ContentOpacity, History, Identity, MergeModel, NativeQuery, OperationalEnvelope, Page, PageShape, ProfileError, Substrate, Translation, Verb, WriteGranularity, ) from shard_wiki.provenance import Liveness, ProvenanceEnvelope def _read_only_folder_profile(**over) -> CapabilityProfile: base = dict( substrate=Substrate.FILES, attachment_mode=AttachmentMode.FILE_STORE, write_granularity=WriteGranularity.NONE, content_opacity=ContentOpacity.TRANSPARENT, operational_envelope=OperationalEnvelope.LOCAL_UNBOUNDED, access_grant=AccessGrant.OPEN, liveness=Liveness.STATIC, history=History.NONE, merge_model=MergeModel.NONE, addressing=Addressing.PATH, native_query=NativeQuery.NONE, translation=Translation.NATIVE, supported_verbs=frozenset({Verb.READ}), ) base.update(over) return CapabilityProfile(**base) def test_identity_is_stable_across_content_and_value_equal(): a = Identity("shardA", "Home") b = Identity("shardA", "Home") assert a == b and hash(a) == hash(b) assert str(a) == "shardA:Home" # Identity does not depend on content: a Page edit reuses the same identity. env = ProvenanceEnvelope(source_shard="shardA") p1 = Page(a, "first body", env) p2 = Page(a, "edited body", env) assert p1.identity == p2.identity # stable handle, not a fingerprint def test_read_only_folder_profile_validates(): assert _read_only_folder_profile().validate().supports(Verb.READ) def test_missing_read_is_rejected(): with pytest.raises(ProfileError, match="READ"): _read_only_folder_profile(supported_verbs=frozenset()).validate() def test_git_is_store_requires_git_substrate_and_history(): with pytest.raises(ProfileError, match="git-is-store"): _read_only_folder_profile(attachment_mode=AttachmentMode.GIT_IS_STORE).validate() # consistent git-is-store profile validates _read_only_folder_profile( attachment_mode=AttachmentMode.GIT_IS_STORE, substrate=Substrate.GIT, history=History.GIT_NATIVE, ).validate() def test_encrypted_forbids_native_query(): with pytest.raises(ProfileError, match="encrypted"): _read_only_folder_profile( content_opacity=ContentOpacity.ENCRYPTED, native_query=NativeQuery.DB_QUERY ).validate() def test_write_verb_requires_write_granularity(): with pytest.raises(ProfileError, match="read-only consistency"): _read_only_folder_profile(supported_verbs=frozenset({Verb.READ, Verb.WRITE})).validate() # writable profile is consistent _read_only_folder_profile( write_granularity=WriteGranularity.PER_PAGE, supported_verbs=frozenset({Verb.READ, Verb.WRITE}), ).validate() def test_native_crdt_merge_requires_crdt_log_and_realtime(): with pytest.raises(ProfileError, match="native-crdt"): _read_only_folder_profile( merge_model=MergeModel.NATIVE_CRDT, supported_verbs=frozenset({Verb.READ, Verb.MERGE}), ).validate() def test_page_defaults_to_prose(): p = Page(Identity("s", "k"), "x", ProvenanceEnvelope(source_shard="s")) assert p.shape is PageShape.PROSE