Fix failing tests, update page component handler to exclude 404 pages on retries

This commit is contained in:
2026-10-03 03:32:01 +01:00
parent 6e6cfef5e5
commit 7bab610b38
8 changed files with 59 additions and 55 deletions
Vendored Executable → Regular
View File
+3 -7
View File
@@ -63,14 +63,12 @@ export default async function grabPageComponent(params) {
// Only retry transient failures: if the last rebuild failed
// (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds
// will just repeat the same error.
while (retries < MAX_RETRIES &&
!global.BUNEXT_BUILD_FAILED) {
while (retries < MAX_RETRIES && !global.BUNEXT_BUILD_FAILED) {
await fullRebuild({
msg: `Retrying Bundle map for file \`${relative_file_path}\``,
});
await Bun.sleep(1000);
bundledMap =
global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
if (bundledMap?.path)
break;
retries++;
@@ -122,9 +120,7 @@ export default async function grabPageComponent(params) {
const is404 = error instanceof NotFoundError ||
error?.name === "NotFoundError" ||
error?.status === 404;
if (!params.retry && !params.no_retry && is_dev) {
// Skip the retry cascade when the last rebuild failed: the error
// is persistent and more rebuilds would repeat it endlessly.
if (!params.retry && !params.no_retry && is_dev && !is404) {
while (!global.BUNEXT_BUILD_FAILED &&
global.BUNEXT_REBUILD_RETRIES < 2) {
global.BUNEXT_REBUILD_RETRIES =
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@moduletrace/bunext",
"version": "1.1.15",
"version": "1.1.16",
"main": "dist/index.js",
"module": "index.ts",
"dependencies": {
+12 -1
View File
@@ -131,7 +131,6 @@ describe("E2E Integration", () => {
if (server) {
server.stop(true);
}
global.BUNEXT_SERVER = undefined;
process.chdir(originalCwd);
@@ -142,6 +141,18 @@ describe("E2E Integration", () => {
delete process.env.PORT;
}
// Reset globals this file mutated so later test files (which share
// the process when bun runs un-isolated) never see fixture state.
global.BUNEXT_DIR_NAMES = undefined as any;
global.BUNEXT_CONFIG = undefined as any;
global.BUNEXT_CONSTANTS = undefined as any;
global.BUNEXT_SERVER = undefined;
global.BUNEXT_ROUTER = undefined as any;
global.BUNEXT_BUNDLER_CTX_MAP = undefined;
global.BUNEXT_SSR_BUNDLER_CTX_MAP = undefined as any;
global.BUNEXT_HMR_CONTROLLERS = [];
global.BUNEXT_REACT_DOM_MODULE_CACHE = new Map<string, any>();
// Remove generated artifacts from the fixture
const dotBunext = path.join(fixtureDir, ".bunext");
if (fs.existsSync(dotBunext)) {
@@ -41,6 +41,7 @@ describe("grabPageComponent", () => {
req: new Request("http://localhost:3000/unknown-foo-bar123"),
});
console.log("DEBUG-404:", res instanceof Response ? "Response" : JSON.stringify(res));
expect(res.serverRes?.response_options?.status).toBe(404);
expect(log.error).not.toHaveBeenCalled();
});
@@ -1,19 +1,9 @@
import { describe, expect, test, beforeEach, afterEach, mock } from "bun:test";
import { describe, expect, test, beforeEach, afterEach } from "bun:test";
const grabPageComponent = mock(async () => ({
serverRes: { props: { ok: true } },
}));
mock.module(
"../../../functions/server/web-pages/grab-page-component",
() => ({
default: grabPageComponent,
}),
);
const { default: serverPostBuildFn } = await import(
"../../../functions/server/server-post-build-fn"
);
// NOTE: this file runs the REAL grabPageComponent (no module mock). The
// callers use `return_server_res_only` + `no_retry`, so no bundling or
// rebuilds happen as long as the fake router + artifact map are set up.
import serverPostBuildFn from "../../../functions/server/server-post-build-fn";
function makeController(page_url: string, target_map?: { local_path: string }) {
const enqueued: string[] = [];
@@ -31,30 +21,41 @@ function makeController(page_url: string, target_map?: { local_path: string }) {
describe("server-post-build-fn", () => {
beforeEach(() => {
grabPageComponent.mockClear();
global.BUNEXT_ROUTER = {
match: (path: string) => {
if (path === "/about")
return { filePath: "/pages/about.tsx" };
if (path === "/new-page")
return { filePath: "/pages/new-page.tsx" };
return null;
},
} as any;
global.BUNEXT_BUNDLER_CTX_MAP = {
"/pages/about.tsx": {
local_path: "/pages/about.tsx",
"pages/about.tsx": {
local_path: "pages/about.tsx",
path: "pages/about.js",
} as any,
"/pages/404.tsx": {
local_path: "/pages/404.tsx",
"pages/404.tsx": {
local_path: "pages/404.tsx",
path: "pages/404.js",
} as any,
};
global.BUNEXT_HMR_CONTROLLERS = [];
global.BUNEXT_ROOT_FILE_UPDATED = false;
global.BUNEXT_IS_404_PAGE = false;
});
afterEach(() => {
global.BUNEXT_ROUTER = undefined as any;
global.BUNEXT_BUNDLER_CTX_MAP = undefined as any;
global.BUNEXT_HMR_CONTROLLERS = [];
global.BUNEXT_ROOT_FILE_UPDATED = false;
global.BUNEXT_IS_404_PAGE = false;
});
test("soft-updates a normal page controller", async () => {
const c = makeController("http://localhost/about", {
local_path: "/pages/about.tsx",
local_path: "pages/about.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [c as any];
@@ -63,13 +64,12 @@ describe("server-post-build-fn", () => {
expect(c.enqueued.length).toBe(1);
expect(c.enqueued[0]).toContain("event: update");
expect(c.enqueued[0]).not.toContain('"reload":true');
expect(c.enqueued[0]).toContain("/pages/about.tsx");
expect(grabPageComponent).toHaveBeenCalled();
expect(c.enqueued[0]).toContain("pages/about.tsx");
});
test("soft-updates unmatched URL via custom 404 artifact", async () => {
const c = makeController("http://localhost/missing", {
local_path: "/pages/404.tsx",
local_path: "pages/404.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [c as any];
@@ -77,13 +77,12 @@ describe("server-post-build-fn", () => {
expect(c.enqueued.length).toBe(1);
expect(c.enqueued[0]).not.toContain('"reload":true');
expect(c.enqueued[0]).toContain("/pages/404.tsx");
expect(grabPageComponent).toHaveBeenCalled();
expect(c.enqueued[0]).toContain("pages/404.tsx");
});
test("full-reloads when the controller's target artifact is missing from the map", async () => {
const c = makeController("http://localhost/gone", {
local_path: "/pages/removed.tsx",
local_path: "pages/removed.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [c as any];
@@ -91,15 +90,14 @@ describe("server-post-build-fn", () => {
expect(c.enqueued.length).toBe(1);
expect(c.enqueued[0]).toContain('"reload":true');
expect(grabPageComponent).not.toHaveBeenCalled();
});
test("full-reloads every controller when reload_all_controllers is set", async () => {
const c1 = makeController("http://localhost/about", {
local_path: "/pages/about.tsx",
local_path: "pages/about.tsx",
});
const c2 = makeController("http://localhost/missing", {
local_path: "/pages/404.tsx",
local_path: "pages/404.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [c1 as any, c2 as any];
@@ -109,12 +107,11 @@ describe("server-post-build-fn", () => {
expect(c1.enqueued[0]).toContain('"reload":true');
expect(c2.enqueued.length).toBe(1);
expect(c2.enqueued[0]).toContain('"reload":true');
expect(grabPageComponent).not.toHaveBeenCalled();
});
test("enqueues reload when the root file changed and resets the flag", async () => {
const c = makeController("http://localhost/about", {
local_path: "/pages/about.tsx",
local_path: "pages/about.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [c as any];
global.BUNEXT_ROOT_FILE_UPDATED = true;
@@ -133,19 +130,18 @@ describe("server-post-build-fn", () => {
await serverPostBuildFn();
expect(c.enqueued.length).toBe(0);
expect(grabPageComponent).not.toHaveBeenCalled();
});
test("only touches controllers whose page is in changed_pages", async () => {
const changed = makeController("http://localhost/about", {
local_path: "/pages/about.tsx",
local_path: "pages/about.tsx",
});
const untouched = makeController("http://localhost/missing", {
local_path: "/pages/404.tsx",
local_path: "pages/404.tsx",
});
global.BUNEXT_HMR_CONTROLLERS = [changed as any, untouched as any];
await serverPostBuildFn({ changed_pages: ["/pages/about.tsx"] });
await serverPostBuildFn({ changed_pages: ["pages/about.tsx"] });
expect(changed.enqueued.length).toBe(1);
expect(untouched.enqueued.length).toBe(0);
@@ -1,4 +1,4 @@
import { describe, expect, test, mock, afterEach } from "bun:test";
import { describe, expect, test, mock, afterEach, afterAll } from "bun:test";
import startServer from "../../../../src/functions/server/start-server";
import { log } from "../../../../src/utils/log";
@@ -29,6 +29,12 @@ describe("startServer", () => {
}
});
afterAll(() => {
// Prevent the module mocks (log, grab-config, grab-app-port) from
// leaking into other test files sharing this process.
mock.restore();
});
test("starts the server and assigns to global.BUNEXT_SERVER", async () => {
global.BUNEXT_CONFIG = { development: true };
@@ -115,16 +115,12 @@ export default async function grabPageComponent(
// Only retry transient failures: if the last rebuild failed
// (BUNEXT_BUILD_FAILED), the file is broken and more rebuilds
// will just repeat the same error.
while (
retries < MAX_RETRIES &&
!global.BUNEXT_BUILD_FAILED
) {
while (retries < MAX_RETRIES && !global.BUNEXT_BUILD_FAILED) {
await fullRebuild({
msg: `Retrying Bundle map for file \`${relative_file_path}\``,
});
await Bun.sleep(1000);
bundledMap =
global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
bundledMap = global.BUNEXT_BUNDLER_CTX_MAP[relative_file_path];
if (bundledMap?.path) break;
retries++;
}
@@ -185,9 +181,7 @@ export default async function grabPageComponent(
error?.name === "NotFoundError" ||
error?.status === 404;
if (!params.retry && !params.no_retry && is_dev) {
// Skip the retry cascade when the last rebuild failed: the error
// is persistent and more rebuilds would repeat it endlessly.
if (!params.retry && !params.no_retry && is_dev && !is404) {
while (
!global.BUNEXT_BUILD_FAILED &&
global.BUNEXT_REBUILD_RETRIES < 2