"""TDD — ui_blob helpers must not read commit_meta (dropped in migration 0020). Covers: B1 _fetch_file_history reads agent_id/model_id/sem_ver_bump/breaking_changes from first-class ORM columns, not commit_meta B2 blob route provenance block reads from first-class columns """ from __future__ import annotations import msgpack import pytest from datetime import datetime, timezone from sqlalchemy.ext.asyncio import AsyncSession from musehub.db import musehub_repo_models as db from muse.core.types import blob_id def _utc() -> datetime: return datetime.now(tz=timezone.utc) async def _make_repo(session: AsyncSession, slug: str) -> str: from musehub.core.genesis import compute_identity_id, compute_repo_id owner = "gabriel" uid = compute_identity_id(owner.encode()) created_at = _utc() repo_id = compute_repo_id(uid, slug, "code", created_at.isoformat()) session.add(db.MusehubRepo( repo_id=repo_id, name=slug, owner=owner, slug=slug, visibility="public", owner_user_id=uid, description="", tags=[], created_at=created_at, )) await session.commit() return repo_id async def _add_snapshot(session: AsyncSession, repo_id: str, seed: str, path: str) -> str: snap_id = blob_id(f"snap-{seed}".encode()) oid = blob_id(f"blob-{seed}".encode()) manifest = {path: oid} session.add(db.MusehubSnapshot( snapshot_id=snap_id, directories=[], manifest_blob=msgpack.packb(manifest, use_bin_type=True), entry_count=1, created_at=_utc(), )) session.add(db.MusehubSnapshotRef(repo_id=repo_id, snapshot_id=snap_id)) await session.commit() return snap_id async def _add_commit( session: AsyncSession, repo_id: str, seed: str, snapshot_id: str, *, agent_id: str = "", model_id: str = "", sem_ver_bump: str = "none", breaking_changes: list[str] | None = None, ) -> db.MusehubCommit: commit_id = blob_id(seed.encode()) row = db.MusehubCommit( commit_id=commit_id, branch="dev", parent_ids=[], message=f"feat: {seed}", author="gabriel", timestamp=_utc(), snapshot_id=snapshot_id, agent_id=agent_id, model_id=model_id, sem_ver_bump=sem_ver_bump, breaking_changes=breaking_changes or [], ) session.add(row) session.add(db.MusehubCommitRef(repo_id=repo_id, commit_id=commit_id)) await session.commit() return row # --------------------------------------------------------------------------- # B1 — _fetch_file_history reads from first-class columns # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_b1_file_history_reads_first_class_columns( db_session: AsyncSession, ) -> None: from musehub.api.routes.musehub.ui_blob import _fetch_file_history repo_id = await _make_repo(db_session, "blob-hist-001") path = "src/app.py" snap_id = await _add_snapshot(db_session, repo_id, "h1", path) commit = await _add_commit( db_session, repo_id, "blob-hist-commit-1", snap_id, agent_id="claude-code", model_id="claude-sonnet-4-6", sem_ver_bump="minor", breaking_changes=[], ) history = await _fetch_file_history(db_session, repo_id, path, commit.commit_id) assert len(history) == 1 entry = history[0] assert entry["is_agent"] is True assert entry["model_label"] != "" assert entry["sem_ver_bump"] == "minor" assert entry["breaking"] is False # --------------------------------------------------------------------------- # B2 — blob route provenance block reads from first-class columns # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_b2_blob_route_provenance_no_commit_meta( db_session: AsyncSession, ) -> None: """The blob route's Phase 1 provenance extraction must not touch commit_meta.""" from musehub.api.routes.musehub import ui_blob repo_id = await _make_repo(db_session, "blob-prov-001") path = "src/main.py" snap_id = await _add_snapshot(db_session, repo_id, "p1", path) commit = await _add_commit( db_session, repo_id, "blob-prov-commit-1", snap_id, agent_id="claude-code", model_id="claude-sonnet-4-6", sem_ver_bump="patch", ) # last_commit is a full MusehubCommit row — directly test the attribute reads # that Phase 1 performs (no commit_meta access) last_commit = commit assert not hasattr(last_commit, "commit_meta"), "commit_meta must not exist on ORM row" commit_agent_id: str = last_commit.agent_id or "" commit_model_id: str = last_commit.model_id or "" commit_sem_ver_bump: str = last_commit.sem_ver_bump or "none" commit_breaking: bool = bool(last_commit.breaking_changes) assert commit_agent_id == "claude-code" assert commit_model_id == "claude-sonnet-4-6" assert commit_sem_ver_bump == "patch" assert commit_breaking is False