fix(build): move a route test out of src/pages so the container build resolves #180

Sammanfogat
supernaut sammanfogade 4 incheckningar från fix/no-tests-in-pages in i main 2026-08-02 20:57:19 +00:00
Ägare

src/pages/api/resend-setup-link.test.ts broke the container build on main. Astro routes every
file under src/pages/ — a .ts file there becomes an API endpoint — so the test file was compiled
as a real route, and its import { vi } from "vitest" became a runtime import of a devDependency.

The Containerfile's build stage installs production dependencies only (pnpm install --frozen-lockfile --prod), so vitest is not there and the build fails:

[vite]: Rolldown failed to resolve import "vitest" from "/app/src/pages/api/resend-setup-link.test.ts".

The test moves to src/test/, out of route space. It is the same test — only the relative paths
change, two levels to one. That rewrite is load-bearing rather than cosmetic: Vitest keys vi.mock
by the resolved module path, so ../../lib/captcha had to become ../lib/captcha to keep
pointing at the same src/lib/captcha the route itself imports. Get it wrong and the suite still
passes while mocking nothing.

src/test/ is a deliberate deviation from this repo's colocation convention (src/lib/x.test.ts
next to src/lib/x.ts). A test for a route cannot be colocated, because the directory it would sit
in is the router.

Verified, not assumed

Both sides reproduced with a real podman build, not inferred from the error text:

tree result
main (d053d09) fails — Rolldown failed to resolve import "vitest"
this branch succeeds — Successfully tagged localhost/bitborg-web:latest

On this branch: pnpm test 293 passing across 23 files (the moved test is still collected —
vitest.config.ts includes src/**/*.test.ts, which covers src/test/), pnpm lint clean,
pnpm check 0 errors and 0 warnings.

Why CI did not catch this, and the guard that now does

Not an oversight in the test suite — a structural blind spot. ci.yml installs with pnpm install --frozen-lockfile, dev dependencies included, then runs pnpm build. Under that install vitest
resolves, so the build succeeds and the route is bundled without complaint. Only the --prod install
in the Containerfile can surface the missing dependency, and that build runs on push to main, after
review. Without a guard the same mistake lands again the next time a test is written next to a route,
and is again invisible until it is already on main.

So eslint.config.mjs gains a "No tests in route space" block: any *.test.* or *.spec.* under
src/pages/ is an error, with the reason and the remedy in the message.

A lint rule rather than a production-install build step in pull-request CI, because it fails earlier
and cheaper — at the lefthook pre-commit hook on the machine where the file was written, before the
mistake reaches a branch, rather than after a full image build in CI.

Also demonstrated rather than asserted — a throwaway test file, added and then removed:

probe before the rule after the rule
src/pages/api/__guard-probe.test.ts pnpm eslint clean 1 error
src/pages/__probe-root.test.ts — 1 error

The second probe checks the glob's zero-segment case, so a test dropped directly into src/pages/
is caught and not only one nested under api/. With both probes removed the tree is clean:
pnpm eslint and pnpm format:check pass.

`src/pages/api/resend-setup-link.test.ts` broke the container build on `main`. Astro routes **every** file under `src/pages/` — a `.ts` file there becomes an API endpoint — so the test file was compiled as a real route, and its `import { vi } from "vitest"` became a runtime import of a devDependency. The Containerfile's build stage installs production dependencies only (`pnpm install --frozen-lockfile --prod`), so `vitest` is not there and the build fails: ``` [vite]: Rolldown failed to resolve import "vitest" from "/app/src/pages/api/resend-setup-link.test.ts". ``` The test moves to `src/test/`, out of route space. It is the same test — only the relative paths change, two levels to one. That rewrite is load-bearing rather than cosmetic: Vitest keys `vi.mock` by the **resolved** module path, so `../../lib/captcha` had to become `../lib/captcha` to keep pointing at the same `src/lib/captcha` the route itself imports. Get it wrong and the suite still passes while mocking nothing. `src/test/` is a deliberate deviation from this repo's colocation convention (`src/lib/x.test.ts` next to `src/lib/x.ts`). A test for a route cannot be colocated, because the directory it would sit in is the router. ### Verified, not assumed Both sides reproduced with a real `podman build`, not inferred from the error text: | tree | result | | ---- | ------ | | `main` (d053d09) | **fails** — `Rolldown failed to resolve import "vitest"` | | this branch | **succeeds** — `Successfully tagged localhost/bitborg-web:latest` | On this branch: `pnpm test` 293 passing across 23 files (the moved test is still collected — `vitest.config.ts` includes `src/**/*.test.ts`, which covers `src/test/`), `pnpm lint` clean, `pnpm check` 0 errors and 0 warnings. ### Why CI did not catch this, and the guard that now does Not an oversight in the test suite — a structural blind spot. `ci.yml` installs with `pnpm install --frozen-lockfile`, dev dependencies included, then runs `pnpm build`. Under that install `vitest` resolves, so the build succeeds and the route is bundled without complaint. Only the `--prod` install in the Containerfile can surface the missing dependency, and that build runs on push to `main`, after review. Without a guard the same mistake lands again the next time a test is written next to a route, and is again invisible until it is already on `main`. So `eslint.config.mjs` gains a "No tests in route space" block: any `*.test.*` or `*.spec.*` under `src/pages/` is an error, with the reason and the remedy in the message. A lint rule rather than a production-install build step in pull-request CI, because it fails earlier and cheaper — at the `lefthook` pre-commit hook on the machine where the file was written, before the mistake reaches a branch, rather than after a full image build in CI. Also demonstrated rather than asserted — a throwaway test file, added and then removed: | probe | before the rule | after the rule | | ----- | --------------- | -------------- | | `src/pages/api/__guard-probe.test.ts` | `pnpm eslint` clean | 1 error | | `src/pages/__probe-root.test.ts` | — | 1 error | The second probe checks the glob's zero-segment case, so a test dropped directly into `src/pages/` is caught and not only one nested under `api/`. With both probes removed the tree is clean: `pnpm eslint` and `pnpm format:check` pass.
supernaut lade till 2 incheckningar 2026-08-02 20:41:24 +00:00
chore: bump version
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m9s
7c97c59a1c
supernaut lade till 1 incheckning 2026-08-02 20:44:46 +00:00
fix(lint): refuse test files under src/pages so route space stays buildable
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m13s
ae36b869b0
The move alone fixes today's break but does not prevent the next one. Pull-request CI
installs with dev dependencies, so a vitest-importing route resolves and `pnpm build`
passes; only the Containerfile's `--prod` install fails, and that runs on push to main.
A lint rule fails on the developer's machine at the pre-commit hook instead.
supernaut lade till 1 incheckning 2026-08-02 20:52:20 +00:00
chore: tweak lefthook globs
Alla kontroller lyckades
ci / ci (pull_request) Successful in 1m14s
90538fd695
supernaut sammanfogade incheckning 4041107aa4 till main 2026-08-02 20:57:19 +00:00
supernaut tog bort grenen fix/no-tests-in-pages 2026-08-02 20:57:20 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-web!180
Ingen beskrivning angiven.