Files
shard-wiki/tests/test_model.py
tegwick 5a77ea879c feat(model): Identity/Placement/Span, Page, CapabilityProfile (WP-0007 T2)
Identity = stable shard-scoped handle (not a fingerprint); Placement separate;
Span carries a layered provenance delta. Page model (PageShape incl. the four
computational shapes). CapabilityProfile with orthogonal-core axes + validate()
applying §6.5 implication rules that reject impossible profiles. Imports only
provenance/. 8 tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 09:00:49 +02:00

107 lines
3.4 KiB
Python

"""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