Files
photo-live/services/api/test/health.e2e-spec.ts
T
浅念 47fb81ad31 chore(api): p1a final-review hygiene items
- contracts: add NOT_FOUND error code (framework-level 404 for unknown
  routes); Error enum + regen committed
- api: 404 default mapping in AllExceptionsFilter now NOT_FOUND (was
  ALBUM_NOT_FOUND); business 404s unchanged via explicit ApiException codes
- api: extract configureApp (prefix/CORS/x-powered-by) shared by main-cn
  bootstrap and e2e; disable x-powered-by, asserted in e2e
- api: move parseCursor/parseLimit (+page-size bounds) from
  albums.controller to shared/pagination; members/uploads/public re-pointed
- infra: bind compose published ports to 127.0.0.1 (mysql/redis/minio);
  .env.example endpoints follow suit
- e2e: unknown-route 404 NOT_FOUND + x-powered-by absent cases added
  (api e2e 120 -> 122)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-18 08:18:30 +08:00

83 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// services/api/test/health.e2e-spec.ts
// e2e 冒烟:验证 e2e 工装(Nest testing module + supertest + 根 .env 加载)可用。
// 不监听端口:app.init() 后直接对 app.getHttpServer() 发请求。
// 应用级配置与 main-cn.ts 共用 src/shared/configure-app(P1a 终审修缮后收敛为
// 单一来源),故本 spec 对响应头/未知路由的断言即生产引导路径的行为:
// · /health 基本形状 + x-request-id 回显/生成/超长替换;
// · 未注册路由 → 404 信封 code=NOT_FOUND(终审修缮①:框架级 404 不再冒充
// ALBUM_NOT_FOUND;业务 404 一律经 ApiException 显式携带域码);
// · 响应不暴露 x-powered-by 指纹头(终审修缮②)。
import { Test } from '@nestjs/testing';
import { type NestExpressApplication } from '@nestjs/platform-express';
import request from 'supertest';
import { AppCnModule } from '../src/app-cn.module';
import { API_PREFIX } from '../src/shared/constants';
import { configureApp } from '../src/shared/configure-app';
import { resetDb } from './reset';
describe(`e2e smoke: /${API_PREFIX}/health & framework defaults`, () => {
let app: NestExpressApplication;
beforeAll(async () => {
await resetDb();
const moduleRef = await Test.createTestingModule({
imports: [AppCnModule],
}).compile();
app = moduleRef.createNestApplication<NestExpressApplication>();
configureApp(app);
await app.init();
});
afterAll(async () => {
await app.close();
});
it('responds 200 {"status":"ok"}', async () => {
const res = await request(app.getHttpServer()).get(`/${API_PREFIX}/health`);
expect(res.status).toBe(200);
expect(res.body).toEqual({ status: 'ok' });
});
it('sets x-request-id response header (generated when absent)', async () => {
const res = await request(app.getHttpServer()).get(`/${API_PREFIX}/health`);
const requestId = res.headers['x-request-id'];
expect(typeof requestId).toBe('string');
expect(requestId.length ?? 0).toBeGreaterThan(0);
});
it('echoes incoming x-request-id header', async () => {
const res = await request(app.getHttpServer())
.get(`/${API_PREFIX}/health`)
.set('x-request-id', 'e2e-fixed-request-id');
expect(res.headers['x-request-id']).toBe('e2e-fixed-request-id');
});
it('replaces an over-long incoming x-request-id (>128 chars) with a fresh one', async () => {
const overlong = 'z'.repeat(200);
const res = await request(app.getHttpServer())
.get(`/${API_PREFIX}/health`)
.set('x-request-id', overlong);
const requestId = res.headers['x-request-id'];
expect(typeof requestId).toBe('string');
expect(requestId).not.toBe(overlong);
expect((requestId as string).length).toBeLessThanOrEqual(128);
});
it('responds 404 error envelope with code NOT_FOUND for unknown routes', async () => {
const res = await request(app.getHttpServer()).get(
`/${API_PREFIX}/nonexistent-route`,
);
expect(res.status).toBe(404);
expect(res.body).toMatchObject({
code: 'NOT_FOUND',
requestId: expect.any(String) as string,
});
expect(typeof res.body.message).toBe('string');
});
it('omits the x-powered-by response header', async () => {
const res = await request(app.getHttpServer()).get(`/${API_PREFIX}/health`);
expect(res.headers['x-powered-by']).toBeUndefined();
});
});