gabriel / musehub public
test_ui_blob_no_commit_meta.py python
147 lines 4.9 KB
Raw
sha256:0997d6250ae6476362f6fe2025af7789f46d03df3e9f34356d5e8ee79b201923 fix(issues): use issue number as pagination cursor, not cre… Sonnet 4.6 patch 8 days ago
1 """TDD — ui_blob helpers must not read commit_meta (dropped in migration 0020).
2
3 Covers:
4 B1 _fetch_file_history reads agent_id/model_id/sem_ver_bump/breaking_changes
5 from first-class ORM columns, not commit_meta
6 B2 blob route provenance block reads from first-class columns
7 """
8 from __future__ import annotations
9
10 import msgpack
11 import pytest
12 from datetime import datetime, timezone
13 from sqlalchemy.ext.asyncio import AsyncSession
14
15 from musehub.db import musehub_repo_models as db
16 from muse.core.types import blob_id
17
18
19 def _utc() -> datetime:
20 return datetime.now(tz=timezone.utc)
21
22
23 async def _make_repo(session: AsyncSession, slug: str) -> str:
24 from musehub.core.genesis import compute_identity_id, compute_repo_id
25 owner = "gabriel"
26 uid = compute_identity_id(owner.encode())
27 created_at = _utc()
28 repo_id = compute_repo_id(uid, slug, "code", created_at.isoformat())
29 session.add(db.MusehubRepo(
30 repo_id=repo_id, name=slug, owner=owner, slug=slug,
31 visibility="public", owner_user_id=uid,
32 description="", tags=[], created_at=created_at,
33 ))
34 await session.commit()
35 return repo_id
36
37
38 async def _add_snapshot(session: AsyncSession, repo_id: str, seed: str, path: str) -> str:
39 snap_id = blob_id(f"snap-{seed}".encode())
40 oid = blob_id(f"blob-{seed}".encode())
41 manifest = {path: oid}
42 session.add(db.MusehubSnapshot(
43 snapshot_id=snap_id,
44 directories=[],
45 manifest_blob=msgpack.packb(manifest, use_bin_type=True),
46 entry_count=1,
47 created_at=_utc(),
48 ))
49 session.add(db.MusehubSnapshotRef(repo_id=repo_id, snapshot_id=snap_id))
50 await session.commit()
51 return snap_id
52
53
54 async def _add_commit(
55 session: AsyncSession,
56 repo_id: str,
57 seed: str,
58 snapshot_id: str,
59 *,
60 agent_id: str = "",
61 model_id: str = "",
62 sem_ver_bump: str = "none",
63 breaking_changes: list[str] | None = None,
64 ) -> db.MusehubCommit:
65 commit_id = blob_id(seed.encode())
66 row = db.MusehubCommit(
67 commit_id=commit_id,
68 branch="dev",
69 parent_ids=[],
70 message=f"feat: {seed}",
71 author="gabriel",
72 timestamp=_utc(),
73 snapshot_id=snapshot_id,
74 agent_id=agent_id,
75 model_id=model_id,
76 sem_ver_bump=sem_ver_bump,
77 breaking_changes=breaking_changes or [],
78 )
79 session.add(row)
80 session.add(db.MusehubCommitRef(repo_id=repo_id, commit_id=commit_id))
81 await session.commit()
82 return row
83
84
85 # ---------------------------------------------------------------------------
86 # B1 — _fetch_file_history reads from first-class columns
87 # ---------------------------------------------------------------------------
88
89 @pytest.mark.asyncio
90 async def test_b1_file_history_reads_first_class_columns(
91 db_session: AsyncSession,
92 ) -> None:
93 from musehub.api.routes.musehub.ui_blob import _fetch_file_history
94
95 repo_id = await _make_repo(db_session, "blob-hist-001")
96 path = "src/app.py"
97 snap_id = await _add_snapshot(db_session, repo_id, "h1", path)
98 commit = await _add_commit(
99 db_session, repo_id, "blob-hist-commit-1", snap_id,
100 agent_id="claude-code", model_id="claude-sonnet-4-6",
101 sem_ver_bump="minor", breaking_changes=[],
102 )
103
104 history = await _fetch_file_history(db_session, repo_id, path, commit.commit_id)
105
106 assert len(history) == 1
107 entry = history[0]
108 assert entry["is_agent"] is True
109 assert entry["model_label"] != ""
110 assert entry["sem_ver_bump"] == "minor"
111 assert entry["breaking"] is False
112
113
114 # ---------------------------------------------------------------------------
115 # B2 — blob route provenance block reads from first-class columns
116 # ---------------------------------------------------------------------------
117
118 @pytest.mark.asyncio
119 async def test_b2_blob_route_provenance_no_commit_meta(
120 db_session: AsyncSession,
121 ) -> None:
122 """The blob route's Phase 1 provenance extraction must not touch commit_meta."""
123 from musehub.api.routes.musehub import ui_blob
124
125 repo_id = await _make_repo(db_session, "blob-prov-001")
126 path = "src/main.py"
127 snap_id = await _add_snapshot(db_session, repo_id, "p1", path)
128 commit = await _add_commit(
129 db_session, repo_id, "blob-prov-commit-1", snap_id,
130 agent_id="claude-code", model_id="claude-sonnet-4-6",
131 sem_ver_bump="patch",
132 )
133
134 # last_commit is a full MusehubCommit row — directly test the attribute reads
135 # that Phase 1 performs (no commit_meta access)
136 last_commit = commit
137 assert not hasattr(last_commit, "commit_meta"), "commit_meta must not exist on ORM row"
138
139 commit_agent_id: str = last_commit.agent_id or ""
140 commit_model_id: str = last_commit.model_id or ""
141 commit_sem_ver_bump: str = last_commit.sem_ver_bump or "none"
142 commit_breaking: bool = bool(last_commit.breaking_changes)
143
144 assert commit_agent_id == "claude-code"
145 assert commit_model_id == "claude-sonnet-4-6"
146 assert commit_sem_ver_bump == "patch"
147 assert commit_breaking is False
File History 1 commit
sha256:0997d6250ae6476362f6fe2025af7789f46d03df3e9f34356d5e8ee79b201923 fix(issues): use issue number as pagination cursor, not cre… Sonnet 4.6 patch 8 days ago