gabriel / musehub public

test_branch_reset.py file-level

at sha256:3 · View file ↗ · Intel ↗

History
1 files
1 commits
0 hotspots
0 🧊 dead
0 💥 blast risk
sha256:0 fix: fall back to any indexed mpack in read_object_bytes when push mpac… · gabriel · Jun 17, 2026
1 """TDD — POST /repos/{repo_id}/branches/{name}/reset
2
3 Gap
4 ---
5 No endpoint exists to repoint a branch head to an arbitrary (earlier) commit.
6 This is needed to undo the corrupt merge commit from bug #36 so the author
7 can re-trigger a clean merge.
8
9 Acceptance criteria
10 -------------------
11 B1 POST reset with a known commit_id updates the branch head and returns
12 the new commit_id.
13 B2 POST reset with an unknown commit_id returns 404.
14 B3 POST reset on an unknown branch name returns 404.
15 B4 Endpoint requires valid MSign auth → 401 without headers.
16 B5 POST reset on an unknown repo returns 404.
17 """
18 from __future__ import annotations
19
20 from datetime import datetime, timezone
21
22 import pytest
23 from httpx import AsyncClient
24 from sqlalchemy import select
25 from sqlalchemy.ext.asyncio import AsyncSession
26
27 from muse.core.types import fake_id
28 from musehub.core.genesis import compute_branch_id
29 from musehub.db.musehub_repo_models import MusehubBranch, MusehubCommit, MusehubCommitRef
30 from musehub.types.json_types import StrDict
31
32
33 # ---------------------------------------------------------------------------
34 # Helpers
35 # ---------------------------------------------------------------------------
36
37
38 async def _create_repo(client: AsyncClient, auth_headers: StrDict, name: str) -> str:
39 resp = await client.post(
40 "/api/repos",
41 json={"name": name, "owner": "testuser", "initialize": False},
42 headers=auth_headers,
43 )
44 assert resp.status_code == 201, resp.text
45 return str(resp.json()["repoId"])
46
47
48 async def _seed_branch(
49 db: AsyncSession,
50 repo_id: str,
51 branch_name: str,
52 *,
53 n_commits: int = 2,
54 ) -> list[str]:
55 """Insert n_commits in a chain on branch_name. Returns commit_ids oldest-first."""
56 commit_ids: list[str] = []
57 parent_ids: list[str] = []
58 for i in range(n_commits):
59 cid = fake_id(f"{repo_id}-{branch_name}-{i}")
60 db.add(MusehubCommit(
61 commit_id=cid,
62 branch=branch_name,
63 parent_ids=parent_ids,
64 message=f"commit {i} on {branch_name}",
65 author="aaronrene",
66 timestamp=datetime.now(tz=timezone.utc),
67 ))
68 db.add(MusehubCommitRef(repo_id=repo_id, commit_id=cid))
69 commit_ids.append(cid)
70 parent_ids = [cid]
71 db.add(MusehubBranch(
72 branch_id=compute_branch_id(repo_id, branch_name),
73 repo_id=repo_id,
74 name=branch_name,
75 head_commit_id=commit_ids[-1],
76 ))
77 await db.commit()
78 return commit_ids
79
80
81 # ---------------------------------------------------------------------------
82 # B1 — reset moves the branch head to the target commit
83 # ---------------------------------------------------------------------------
84
85
86 @pytest.mark.asyncio
87 async def test_b1_reset_updates_branch_head(
88 client: AsyncClient,
89 auth_headers: StrDict,
90 db_session: AsyncSession,
91 ) -> None:
92 """B1: POST reset with a valid commit_id updates head and returns it.
93
94 RED: endpoint does not exist.
95 GREEN: branch.head_commit_id is updated to the requested commit.
96 """
97 repo_id = await _create_repo(client, auth_headers, "reset-main-repo")
98 commit_ids = await _seed_branch(db_session, repo_id, "main", n_commits=2)
99 old_head, new_head = commit_ids[1], commit_ids[0] # reset back one commit
100
101 resp = await client.post(
102 f"/api/repos/{repo_id}/branches/main/reset",
103 json={"commitId": new_head},
104 headers=auth_headers,
105 )
106 assert resp.status_code == 200, resp.text
107 body = resp.json()
108 assert body["branch"] == "main"
109 assert body["commitId"] == new_head
110 assert body["previousCommitId"] == old_head
111
112 # Verify the DB row was actually updated.
113 row = (await db_session.execute(
114 select(MusehubBranch).where(
115 MusehubBranch.repo_id == repo_id,
116 MusehubBranch.name == "main",
117 )
118 )).scalar_one()
119 assert row.head_commit_id == new_head
120
121
122 # ---------------------------------------------------------------------------
123 # B2 — reset to an unknown commit_id returns 404
124 # ---------------------------------------------------------------------------
125
126
127 @pytest.mark.asyncio
128 async def test_b2_reset_unknown_commit_returns_404(
129 client: AsyncClient,
130 auth_headers: StrDict,
131 db_session: AsyncSession,
132 ) -> None:
133 """B2: POST reset with a commit_id not in the store returns 404."""
134 repo_id = await _create_repo(client, auth_headers, "reset-bad-commit-repo")
135 await _seed_branch(db_session, repo_id, "main")
136
137 resp = await client.post(
138 f"/api/repos/{repo_id}/branches/main/reset",
139 json={"commitId": fake_id("ghost-commit")},
140 headers=auth_headers,
141 )
142 assert resp.status_code == 404, resp.text
143
144
145 # ---------------------------------------------------------------------------
146 # B3 — reset on an unknown branch name returns 404
147 # ---------------------------------------------------------------------------
148
149
150 @pytest.mark.asyncio
151 async def test_b3_reset_unknown_branch_returns_404(
152 client: AsyncClient,
153 auth_headers: StrDict,
154 db_session: AsyncSession,
155 ) -> None:
156 """B3: POST reset on a branch that doesn't exist returns 404."""
157 repo_id = await _create_repo(client, auth_headers, "reset-no-branch-repo")
158 commit_ids = await _seed_branch(db_session, repo_id, "main")
159
160 resp = await client.post(
161 f"/api/repos/{repo_id}/branches/nonexistent/reset",
162 json={"commitId": commit_ids[0]},
163 headers=auth_headers,
164 )
165 assert resp.status_code == 404, resp.text
166
167
168 # ---------------------------------------------------------------------------
169 # B4 — auth required
170 # ---------------------------------------------------------------------------
171
172
173 @pytest.mark.asyncio
174 async def test_b4_reset_requires_auth(client: AsyncClient) -> None:
175 """B4: POST reset without auth returns 401."""
176 resp = await client.post(
177 f"/api/repos/{fake_id('r')}/branches/main/reset",
178 json={"commitId": fake_id("c")},
179 )
180 assert resp.status_code == 401
181
182
183 # ---------------------------------------------------------------------------
184 # B5 — unknown repo returns 404
185 # ---------------------------------------------------------------------------
186
187
188 @pytest.mark.asyncio
189 async def test_b5_reset_unknown_repo_returns_404(
190 client: AsyncClient,
191 auth_headers: StrDict,
192 ) -> None:
193 """B5: POST reset on an unknown repo_id returns 404."""
194 resp = await client.post(
195 f"/api/repos/{fake_id('ghost-repo')}/branches/main/reset",
196 json={"commitId": fake_id("c")},
197 headers=auth_headers,
198 )
199 assert resp.status_code == 404, resp.text