From 4ae32c4f3b576e9c78df47f24d0b5e06f7e2cd85 Mon Sep 17 00:00:00 2001 From: dirtydishes Date: Sat, 30 May 2026 01:44:45 -0400 Subject: [PATCH] stabilize forgejo ci bun path and mocks --- apps/web/app/routes.test.ts | 3 +- apps/web/app/terminal.test.ts | 16 +- .../2026-05-30-fix-forgejo-ci-test-mocks.html | 260 ++++++++++++++++++ 3 files changed, 275 insertions(+), 4 deletions(-) create mode 100644 docs/turns/2026-05-30-fix-forgejo-ci-test-mocks.html diff --git a/apps/web/app/routes.test.ts b/apps/web/app/routes.test.ts index e217748..5206d51 100644 --- a/apps/web/app/routes.test.ts +++ b/apps/web/app/routes.test.ts @@ -4,7 +4,8 @@ const redirect = mock((path: string) => { throw new Error(`NEXT_REDIRECT:${path}`); }); -mock.module("next/navigation", () => ({ redirect })); +mock.module("next/navigation", () => ({ default: { redirect }, redirect })); +mock.module("next/navigation.js", () => ({ default: { redirect }, redirect })); describe("legacy page redirects", () => { beforeEach(() => { diff --git a/apps/web/app/terminal.test.ts b/apps/web/app/terminal.test.ts index e6ed106..27f376e 100644 --- a/apps/web/app/terminal.test.ts +++ b/apps/web/app/terminal.test.ts @@ -1,6 +1,16 @@ -import { describe, expect, it } from "bun:test"; +import { describe, expect, it, mock } from "bun:test"; import { getSubscriptionKey as getLiveSubscriptionKey } from "@islandflow/types"; -import { + +const redirect = mock((path: string) => { + throw new Error(`NEXT_REDIRECT:${path}`); +}); + +mock.module("next/navigation", () => ({ + redirect, + usePathname: () => "/options" +})); + +const { NAV_ITEMS, appendHistoryTail, buildAlertContextPath, @@ -49,7 +59,7 @@ import { resolveAlertFlowPacket, statusLabel, toggleFilterValue -} from "./terminal"; +} = await import("./terminal"); const makeItem = (traceId: string, seq: number, ts: number) => ({ trace_id: traceId, diff --git a/docs/turns/2026-05-30-fix-forgejo-ci-test-mocks.html b/docs/turns/2026-05-30-fix-forgejo-ci-test-mocks.html new file mode 100644 index 0000000..9432604 --- /dev/null +++ b/docs/turns/2026-05-30-fix-forgejo-ci-test-mocks.html @@ -0,0 +1,260 @@ + + + + + + Fix Forgejo CI test mocks and Bun path handling + + + +
+
+
Turn document
+

Fix Forgejo CI test mocks and Bun path handling

+

Tightened the CI-facing web tests and Bun resolution path so Forgejo can install dependencies, run the typecheck helper, and execute the web test suite without shell PATH surprises.

+
+ Created: 2026-05-30 01:42 EDT + Beads: islandflow-3l6 + Validation: local typecheck + test suite passed +
+
+ +
+

Summary

+

Forgejo was failing in two places: first because the CI shell could not reliably find bun when a helper script spawned it, and then because two web tests depended on Next.js navigation module shapes that did not hold up in the CI runtime. The fix makes the typecheck helper invoke the current Bun executable directly and adjusts the affected mocks to match the module forms used during test execution.

+
+ +
+

Changes Made

+
    +
  • Changed scripts/typecheck.ts to spawn the current Bun executable instead of assuming bunx is reachable on PATH.
  • +
  • Added $HOME/.bun/bin to $GITHUB_PATH in .forgejo/workflows/ci.yml so shell-invoked package scripts can find Bun during the workflow.
  • +
  • Expanded the next/navigation mock in apps/web/app/routes.test.ts to cover both module entry points and expose redirect in the shape the app expects.
  • +
  • Updated apps/web/app/terminal.test.ts to mock next/navigation before importing the terminal module, including a pathname stub and redirect helper for the CI runtime.
  • +
+
+ +
+

Context

+

The repo uses Bun-first tooling and Forgejo as the canonical remote. The CI workflow installs Bun by absolute path, but some helper scripts and package-level commands still assume a PATH-visible Bun binary. On the web side, the terminal and route tests were sensitive to how Bun resolved Next.js module mocks, so the failures only showed up in the CI-shaped run.

+
+ +
+

Important Implementation Details

+
    +
  • scripts/typecheck.ts now uses process.execPath so it stays anchored to the Bun runtime that launched the script.
  • +
  • The CI workflow change is defensive, it keeps any later shell step from depending on a hidden PATH assumption.
  • +
  • The route test mock covers both next/navigation and next/navigation.js, which avoids the module-shape mismatch that appeared in the full suite.
  • +
  • terminal.test.ts now installs the mock first and then dynamically imports the terminal module, which matches the order Bun needs for module interception.
  • +
+
+ +
+

Relevant Diff Snippets

+

Rendered with @pierre/diffs/ssr. The first fragment is the full rendered output for the routes test change. The second fragment reuses the same rendered markup shape for the terminal test change after stripping the duplicate style prelude so the page stays readable.

+
apps/web/app/routes.test.ts
-1+2
3 unmodified lines
4
5
6
7
8
9
10
3 unmodified lines
throw new Error(`NEXT_REDIRECT:${path}`);
});
+
mock.module("next/navigation", () => ({ redirect }));
+
describe("legacy page redirects", () => {
beforeEach(() => {
3 unmodified lines
4
5
6
7
8
9
10
11
3 unmodified lines
throw new Error(`NEXT_REDIRECT:${path}`);
});
+
mock.module("next/navigation", () => ({ default: { redirect }, redirect }));
mock.module("next/navigation.js", () => ({ default: { redirect }, redirect }));
+
describe("legacy page redirects", () => {
beforeEach(() => {
+
apps/web/app/terminal.test.ts
-3+13
1
2
3
4
5
6
42 unmodified lines
49
50
51
52
53
54
55
import { describe, expect, it } from "bun:test";
import { getSubscriptionKey as getLiveSubscriptionKey } from "@islandflow/types";
import {
NAV_ITEMS,
appendHistoryTail,
buildAlertContextPath,
42 unmodified lines
resolveAlertFlowPacket,
statusLabel,
toggleFilterValue
} from "./terminal";
+
const makeItem = (traceId: string, seq: number, ts: number) => ({
trace_id: traceId,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
42 unmodified lines
59
60
61
62
63
64
65
import { describe, expect, it, mock } from "bun:test";
import { getSubscriptionKey as getLiveSubscriptionKey } from "@islandflow/types";
+
const redirect = mock((path: string) => {
throw new Error(`NEXT_REDIRECT:${path}`);
});
+
mock.module("next/navigation", () => ({
redirect,
usePathname: () => "/options"
}));
+
const {
NAV_ITEMS,
appendHistoryTail,
buildAlertContextPath,
42 unmodified lines
resolveAlertFlowPacket,
statusLabel,
toggleFilterValue
} = await import("./terminal");
+
const makeItem = (traceId: string, seq: number, ts: number) => ({
trace_id: traceId,
+
+ +
+

Expected Impact for End-Users

+

Contributors should see Forgejo fail less often on environment-specific Bun lookup issues, and the web test suite should stay stable under the same runtime shape the CI runner uses. That means fewer false negatives and a clearer path from local validation to a green pipeline.

+
+ +
+

Validation

+
    +
  • env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun run typecheck passed.
  • +
  • env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun test passed: 250 tests, 0 failures.
  • +
  • env PATH="$HOME/.bun/bin:/usr/bin:/bin" bun run check:docker-workspace passed in the earlier CI recovery pass.
  • +
+
+ +
+

Issues, Limitations, and Mitigations

+

The current fix addresses the CI failure path that was blocking the workflow. It does not change the wider Next.js testing strategy, so if more module-shape drift appears later, the same pattern may need to be applied to adjacent tests. The workflow path fix is intentionally narrow and should not affect local development outside the CI shell.

+
+ +
+

Follow-up Work

+
    +
  • Watch the next Forgejo run on this branch to confirm the CI path stays clean under the exact runner environment.
  • +
  • Fold any other CI-only Next.js mock quirks into shared helpers if more tests start to depend on the same module shape.
  • +
  • Close out the Beads issue once the Forgejo result is confirmed.
  • +
+
+
+ + \ No newline at end of file