singularity-forge/web/lib/__tests__/boot-null-project.test.ts
Mikael Hugo 6725a55591 feat(web): add error boundaries, expand test coverage, add README
- Add class-based ErrorBoundary component wrapping all 7 main views
  inside WorkspaceChrome; fallback shows view name, error, reload button
- Add 30 new unit tests (boot null-project path × 9, onboarding
  pure-function logic × 21); all 43 web/lib tests pass
- Add web/README.md: architecture, auth flow, 7 views, dev setup,
  API route pattern, test instructions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:24:40 +02:00

102 lines
3.2 KiB
TypeScript

import assert from "node:assert/strict";
import { describe, test } from "vitest";
/**
* Tests for the boot route's null-project path.
*
* When no SF_WEB_PROJECT_CWD is set and no ?project param is present,
* the boot route returns a canned "no project" payload so the frontend
* can show the project picker. These tests verify the shape and values
* of that payload without requiring bridge/subprocess.
*
* See: web/app/api/boot/route.ts — null-project early return branch.
*/
/** Mirrors the null-project payload returned by the boot route. */
function makeNullProjectPayload() {
return {
project: null,
workspace: null,
auto: null,
onboarding: { locked: false },
onboardingNeeded: false,
resumableSessions: [],
bridge: null,
projectDetection: null,
};
}
/**
* Mirrors resolveProjectCwd URL-parsing logic from bridge-service.ts.
* Returns the ?project query param value, or null if absent/malformed.
*/
function resolveProjectFromUrl(requestUrl: string): string | null {
try {
const url = new URL(requestUrl);
const projectParam = url.searchParams.get("project");
if (projectParam) return decodeURIComponent(projectParam);
} catch {
// Malformed URL — fall through
}
return null;
}
describe("boot route — null-project payload shape", () => {
test("nullProjectPayload_always_hasNullTopLevelFields", () => {
const payload = makeNullProjectPayload();
assert.equal(payload.project, null);
assert.equal(payload.workspace, null);
assert.equal(payload.auto, null);
assert.equal(payload.bridge, null);
assert.equal(payload.projectDetection, null);
});
test("nullProjectPayload_always_hasOnboardingNotLocked", () => {
const payload = makeNullProjectPayload();
assert.deepEqual(payload.onboarding, { locked: false });
});
test("nullProjectPayload_always_hasOnboardingNeededFalse", () => {
const payload = makeNullProjectPayload();
assert.equal(payload.onboardingNeeded, false);
});
test("nullProjectPayload_always_hasEmptyResumableSessions", () => {
const payload = makeNullProjectPayload();
assert.deepEqual(payload.resumableSessions, []);
});
});
describe("boot route — resolveProjectCwd URL parsing", () => {
test("resolveProjectFromUrl_whenProjectParamPresent_returnsDecodedValue", () => {
const result = resolveProjectFromUrl(
"http://localhost:3000/api/boot?project=%2Fhome%2Fuser%2Fproject",
);
assert.equal(result, "/home/user/project");
});
test("resolveProjectFromUrl_whenNoProjectParam_returnsNull", () => {
const result = resolveProjectFromUrl("http://localhost:3000/api/boot");
assert.equal(result, null);
});
test("resolveProjectFromUrl_whenProjectParamIsEmpty_returnsNull", () => {
// Empty ?project= param → searchParams.get returns "" → falsy → null
const result = resolveProjectFromUrl(
"http://localhost:3000/api/boot?project=",
);
assert.equal(result, null);
});
test("resolveProjectFromUrl_whenUrlIsMalformed_returnsNull", () => {
const result = resolveProjectFromUrl("not-a-valid-url");
assert.equal(result, null);
});
test("resolveProjectFromUrl_whenProjectParamHasSpaces_returnsDecodedPath", () => {
const result = resolveProjectFromUrl(
"http://localhost:3000/api/boot?project=%2Fhome%2Fmy%20project",
);
assert.equal(result, "/home/my project");
});
});