mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
0257e4e71e
First strangler migration (L1): /api/weather is served by a NestJS module.
- @trek/shared/weather Zod contract; Nest controller byte-identical to the legacy Express route (paths, query params, status codes, { error } bodies, lang default, ApiError/500 passthrough). Service reuses getWeather/getDetailedWeather (+ shared cache; MCP tools unchanged).
- Strangler routes /api/weather to Nest by default; the legacy Express route + its migration-time parity test were decommissioned in this PR.
- Frontend (FE2): weatherApi typed against the @trek/shared WeatherResult contract.
- Harness: reusable Nest-vs-Express parity harness, e2e harness (temp SQLite + seed/cookie helpers, real JwtAuthGuard), src/nest coverage gate raised to >=80%, src/nest test guide.
- Verified end-to-end on a prod mirror (dev1): 401/400/200 via Nest with real Open-Meteo data, Express route gone.
40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
import request from 'supertest';
|
|
import { expect } from 'vitest';
|
|
import type { Server } from 'http';
|
|
|
|
export interface ParityRequest {
|
|
method?: 'get' | 'post' | 'put' | 'patch' | 'delete';
|
|
path: string;
|
|
query?: Record<string, string>;
|
|
body?: unknown;
|
|
}
|
|
|
|
/**
|
|
* Reusable Nest-vs-Express parity harness.
|
|
*
|
|
* Fires the same HTTP request at the legacy Express app and the migrated Nest app
|
|
* and asserts the response is client-identical — same status code and same JSON
|
|
* body. With the underlying service mocked identically for both, any difference is
|
|
* purely framework-layer (routing, validation, error envelope), which is exactly
|
|
* what a migration must not change. Use one assertion per migrated route/case.
|
|
*/
|
|
export async function expectParity(
|
|
expressServer: Server | Express.Application,
|
|
nestServer: Server,
|
|
req: ParityRequest,
|
|
): Promise<void> {
|
|
const fire = (target: Server | Express.Application) => {
|
|
const method = req.method ?? 'get';
|
|
let r = request(target as never)[method](req.path);
|
|
if (req.query) r = r.query(req.query);
|
|
if (req.body !== undefined) r = r.send(req.body as object);
|
|
return r;
|
|
};
|
|
|
|
const [ex, ne] = await Promise.all([fire(expressServer), fire(nestServer)]);
|
|
|
|
const label = `${(req.method ?? 'GET').toUpperCase()} ${req.path}`;
|
|
expect(ne.status, `${label}: status mismatch`).toBe(ex.status);
|
|
expect(ne.body, `${label}: body mismatch`).toEqual(ex.body);
|
|
}
|