learning_ai_notes/backend/src/test-helpers.ts
saravanakumardb1 79e936bd68 feat(ci): Cosmos emulator smoke job exercises partition-key paths
The existing 380-test backend suite runs entirely against the in-memory
datastore provider, which treats every partition-key value as equivalent.
This hid one entire class of bug — partition-key mismatches — until
production. D7 closes that gap.

Implementation:

- backend/src/test-helpers.ts adds useCosmosDatastore() that swaps the
  active provider for CosmosDatastoreProvider using COSMOS_ENDPOINT /
  COSMOS_KEY / COSMOS_DATABASE. Throws synchronously when env is missing
  so a misconfigured run fails loudly instead of silently falling back
  to in-memory.

- backend/vitest.config.ts now excludes src/**/*.cosmos.test.ts so the
  default 'pnpm test' run stays green for contributors without Docker.

- backend/vitest.cosmos.config.ts (new) includes ONLY *.cosmos.test.ts,
  bumps testTimeout to 30s / hookTimeout to 60s for the real client
  round-trips, and locks DB_PROVIDER=cosmos in test env.

- backend/src/cosmos.smoke.cosmos.test.ts (new) covers the four most
  important partition-key contracts in NoteLett:
    workspaces      /userId
    notes           /workspaceId
    note_tasks      /workspaceId
    note_shares     /workspaceId  (full create → resolve → delete → null)
  Each test also asserts that a wrong-partition-key lookup returns null,
  which is the failure mode the in-memory provider cannot simulate.

- backend/package.json adds 'test:cosmos' script.

- .github/workflows/ci.yml gains a backend-cosmos job that boots the
  official mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
  container as a service, waits for it to be ready (60 × 5s polls of
  /_explorer/emulator.pem), then runs pnpm test:cosmos against it.
  The job depends on the existing backend job so the emulator only
  spins up after unit tests pass.

Verified locally:
- pnpm --filter @notelett/backend test: 380/380 (cosmos suite excluded)
- vitest list --config vitest.cosmos.config.ts: 4 tests under the cosmos
  smoke suite, as designed
- pnpm run verify: end-to-end green (backend 380/380, web 96/96,
  mobile 97/97)
- ci.yml passes Python yaml.safe_load

CI verification: the new job will execute on the next push. Local
verification against the emulator requires Docker on the dev host.
2026-05-23 00:25:24 -07:00

47 lines
1.5 KiB
TypeScript

import Fastify from 'fastify';
import { CosmosDatastoreProvider, MemoryDatastoreProvider } from '@bytelyst/datastore';
import { setProvider } from './lib/datastore.js';
export function resetMemoryDatastore(): void {
const provider = new MemoryDatastoreProvider();
setProvider(provider);
}
/**
* Bind the active datastore to a live Azure Cosmos endpoint. Only used by
* `*.cosmos.test.ts` suites running under `vitest.cosmos.config.ts`, which
* is gated on COSMOS_ENDPOINT being set (CI: cosmos-emulator service).
*
* Throws synchronously if the required env vars are missing so individual
* tests fail loudly instead of silently falling back to in-memory.
*/
export function useCosmosDatastore(): void {
if (!process.env.COSMOS_ENDPOINT || !process.env.COSMOS_KEY) {
throw new Error('useCosmosDatastore() requires COSMOS_ENDPOINT and COSMOS_KEY to be set');
}
const provider = new CosmosDatastoreProvider({
endpoint: process.env.COSMOS_ENDPOINT,
key: process.env.COSMOS_KEY,
database: process.env.COSMOS_DATABASE || 'notelett_test',
});
setProvider(provider);
}
export async function buildTestApp(
routePlugin: (app: ReturnType<typeof Fastify>) => Promise<void>,
) {
resetMemoryDatastore();
const app = Fastify({ logger: false });
await app.register(routePlugin, { prefix: '/api' });
await app.ready();
return app;
}
export const TEST_USER_ID = 'test-user-1';
export const TEST_PRODUCT_ID = 'notelett';
export function authHeader() {
return { authorization: 'Bearer test-token' };
}