Fix two flaky tests that made full-suite runs unreliable

4d4bd06 · Gabriel Morell · 2026-09-10 15:50

2 files +39 -14
Message
{commit_body(@commit)}

Files changed

modified test/git_gud/forks_test.exs
+28 −12
@@ -115,7 +115,7 @@ defmodule GitGud.ForksTest do
115 115 {:ok, fork} = Repositories.fork(upstream, forker, forker)
116 116
117 117 # Upstream advances; fork stays put.
118 seed_two_commits_on_main(upstream)
118 + advance_upstream_main(upstream)
119 119
120 120 assert {:ok, %{behind: behind, ahead: 0, fast_forward?: true}} =
121 121 Repositories.fork_status(fork, "main")
@@ -151,7 +151,7 @@ defmodule GitGud.ForksTest do
151 151
152 152 :ok = Git.update_ref(fork.disk_path, "refs/heads/main", fork_new, fork_main)
153 153
154 seed_two_commits_on_main(upstream)
154 + advance_upstream_main(upstream)
155 155
156 156 assert {:ok, %{ahead: 1}} = Repositories.fork_status(fork, "main")
157 157 assert {:error, {:diverged, _}} = Repositories.sync_fork(fork, "main", forker)
@@ -164,7 +164,7 @@ defmodule GitGud.ForksTest do
164 164
165 165 forker = user_fixture()
166 166 {:ok, fork} = Repositories.fork(upstream, forker, forker)
167 seed_two_commits_on_main(upstream)
167 + advance_upstream_main(upstream)
168 168
169 169 stranger = user_fixture()
170 170 assert {:error, :forbidden} = Repositories.sync_fork(fork, "main", stranger)
@@ -182,7 +182,7 @@ defmodule GitGud.ForksTest do
182 182 test "pushing to the fork's PR branch updates refs/pull/N/head on upstream" do
183 183 {_upstream_user, upstream} = repository_fixture()
184 184 {:ok, upstream} = Repositories.update_repository(upstream, %{"visibility" => "public"})
185 seed_two_commits_on_main(upstream)
185 + advance_upstream_main(upstream)
186 186
187 187 forker = user_fixture()
188 188 {:ok, fork} = Repositories.fork(upstream, forker, forker)
@@ -268,7 +268,7 @@ defmodule GitGud.ForksTest do
268 268 test "create + mergeable + merge against a fork's branch" do
269 269 {upstream_user, upstream} = repository_fixture()
270 270 {:ok, upstream} = Repositories.update_repository(upstream, %{"visibility" => "public"})
271 seed_two_commits_on_main(upstream)
271 + advance_upstream_main(upstream)
272 272
273 273 forker = user_fixture()
274 274 {:ok, fork} = Repositories.fork(upstream, forker, forker)
@@ -314,18 +314,34 @@ defmodule GitGud.ForksTest do
314 314 commit_sha
315 315 end
316 316
317 defp seed_two_commits_on_main(repo) do
318 sha1 = seed_commit(repo)
317 + # Move main forward one commit from wherever it already points.
318 + #
319 + # This used to re-run `seed_commit/1` first, which builds a *root*
320 + # commit from fixed content: within the same second as the original
321 + # seed that reproduced the identical SHA and main didn't move, but
322 + # across a second boundary it created a different root and rewrote
323 + # history under any fork — turning an expected ahead/behind of 1/1
324 + # into 2/2. Building on the existing tip is deterministic.
325 + defp advance_upstream_main(repo) do
326 + # Some callers hand this a repo with no main yet and rely on it to
327 + # create one; others have already seeded it and just want it moved
328 + # on. Both end up one commit further along than they started.
329 + base =
330 + case Git.resolve(repo.disk_path, "main") do
331 + {:ok, sha} -> sha
332 + _ -> seed_commit(repo)
333 + end
334 +
319 335 blob = write_blob(repo.disk_path, "v2\n")
320 tree = make_tree(repo.disk_path, sha1, "README.md", blob)
336 + tree = make_tree(repo.disk_path, base, "README.md", blob)
321 337
322 {:ok, sha2} =
323 Git.commit_tree(repo.disk_path, tree, [sha1], "second",
338 + {:ok, sha} =
339 + Git.commit_tree(repo.disk_path, tree, [base], "second",
324 340 author: {"t", "t@t", DateTime.utc_now(:second)}
325 341 )
326 342
327 :ok = Git.update_ref(repo.disk_path, "refs/heads/main", sha2, sha1)
328 sha2
343 + :ok = Git.update_ref(repo.disk_path, "refs/heads/main", sha, base)
344 + sha
329 345 end
330 346
331 347 defp add_feature_branch(repo) do
modified test/git_gud/workflows/runner_jwt_test.exs
+11 −2
@@ -48,8 +48,17 @@ defmodule GitGud.Workflows.RunnerJwtTest do
48 48 test "tampered token fails verify", %{job: job, run: run} do
49 49 token = RunnerJwt.mint(job, run)
50 50 [h, p, s] = String.split(token, ".")
51 # Flip one byte of the signature
52 tampered = h <> "." <> p <> "." <> String.slice(s, 0..-2//1) <> "X"
51 + # Flip the *first* character of the signature. The last one is a
52 + # poor choice: 32 bytes encode to 43 base64url chars, so the final
53 + # char carries 4 significant bits and 2 of padding — four different
54 + # characters decode to the same bytes, and swapping between them
55 + # leaves the signature genuinely unaltered. Every bit of the first
56 + # character counts.
57 + <<first::binary-size(1), rest::binary>> = s
58 + replacement = if first == "A", do: "B", else: "A"
59 + tampered = h <> "." <> p <> "." <> replacement <> rest
60 +
61 + assert tampered != token
53 62 assert {:error, _} = RunnerJwt.verify(tampered)
54 63 end
55 64

Parents: a7fb497