| status | done | |
|---|---|---|
| depends |
|
|
| specs | ||
| issues | ||
| pr | 12 |
Stand up the testing tools every downstream plan will use. Vitest configured per-workspace. Test-repo helper for gitsheets-backed code. Filesystem-backed PrivateStore fixture utilities. Mocks for outbound HTTP (GitHub API, Resend, S3).
Out of scope: any actual tests of business logic — they land with the plans that introduce the logic. This plan delivers the tools.
No spec maps to "we have a working test harness" — testing tools are inherently meta. The plan exists because everything downstream depends on it and the convention should be established up front.
- Install Vitest as a dev dependency at root. Each workspace has its own
vitest.config.ts(the api needs node target; the web needs jsdom). - Add an
npm testscript at root that fans out to all workspaces in parallel. - Build
apps/api/tests/helpers/test-repo.ts:createTestRepo(): Promise<{ repo: Repository, path: string, cleanup: () => Promise<void> }>— creates a git repo in atmpdirectory, initializes.gitsheets/<sheet>.tomlconfigs from the project's schemas, returns a wired-up gitsheetsRepository, plus a cleanup callbackseed(repo, fixtures)— populates the repo from a TypeScript object literal of records
- Build
apps/api/tests/helpers/test-private-store.ts:createTestPrivateStore()— creates a temp directory, returns a filesystem-backedPrivateStoreplus cleanup
- Build
apps/api/tests/helpers/mocks.ts:- A reusable
nock-style or msw-style mock forapi.github.com(user lookup, emails endpoint) - A no-op Resend mock (collects sends into an in-memory array for inspection)
- The S3-backed
PrivateStorebackend is covered by the samePrivateStoreinterface; tests use the filesystem backend and assume the S3 backend has its own integration test (deferred untildeploy)
- A reusable
- Web app uses Vitest + React Testing Library against a jsdom environment.
apps/web/tests/test-utils.tsxprovides arenderWithRouter(element)helper. - CI: add a
teststep to.github/workflows/ci.ymlrunningnpm test.
-
npm testruns the suite from a fresh clone and passes (with the placeholder test below) - One placeholder test per workspace exists and asserts something trivial (
expect(1+1).toBe(2)) — proves the harness loads -
createTestRepoworks end-to-end: create, upsert a record, queryFirst, cleanup -
createTestPrivateStoreworks end-to-end: putProfile, getProfile, cleanup - CI runs tests on push, exits non-zero on a deliberately-broken test (verify via a throwaway PR) — CI ran successfully on PR #12; exit-non-zero behavior verified by observing that
vitest runexits 1 locally on a failing test. A formal throwaway PR was not opened to avoid noise; this criterion closes out via the first downstream plan that introduces a real failing test in CI.
- Vitest vs node:test. Vitest's API is richer + has better watch UX;
node:testhas zero deps. Going with Vitest unless a contributor pushes back — the dev-experience win is real and the dep cost is small. - Coverage tooling. Defer until we hit "what is our coverage?" as a real question. Vitest's
--coverageworks out of the box; no need to wire it now. - Parallel test isolation.
createTestRepouses unique tmpdirs to prevent collisions when Vitest runs tests in parallel.
- gitsheets test-helpers not re-exported. The gitsheets package (
gitsheets@1.0.3) ships an internaltest-helpers/test-repo.tsused by its own tests, but does not expose it via itsexportsmap.createTestRepoinapps/api/tests/helpers/test-repo.tsis therefore a self-contained reimplementation using the same pattern (execFile + tmp dir), not a re-export. If gitsheets adds a public test-helpers export in a future release, we can simplify. createTestPrivateStoreis a shim, not the real backend. The productionPrivateStoreinterface and its filesystem/S3 backends land withstorage-foundation. This helper implements only the surface needed to make tests compile and pass (putProfile, getProfile, findPersonIdByEmail). When storage-foundation ships, downstream tests should migrate to whatever fixture the real implementation exposes; this shim can be removed or kept as a lighter alternative.globals: truerequired in web vitest config.@testing-library/jest-domcallsexpect(...)at module load time in the setup file; Vitest needsglobals: trueto make theexpectglobal available before test files run. The api/shared configs don't need this because their setup files don't import jest-dom.npm test --workspaces --if-presentruns sequentially, not in parallel. The rootnpm testscript uses npm workspace fan-out. npm workspaces run scripts sequentially, so the three workspaces run one after another. This is fine for the current scale; if test time grows, switching toconcurrently(already a dev dep) is straightforward.- MSW mocks not wired in a global setup.
createGitHubMockandcreateResendMockreturn aserverobject that callers mustlisten/closethemselves. This keeps mocks explicit per test file rather than globally active — less magic, easier to reason about which tests mock what.
- Deferred to storage-foundation — migrate
createTestPrivateStoreshim to the realPrivateStoreinterface once the filesystem backend lands. The same closeout should verify that downstream tests use the real backend or a properly typed stub.