Files
gitmost/apps/server/src/integrations/environment/environment.service.spec.ts
claude_code 92d03f1ff6 test(server): cover returnToken body opt-in and CORS/Swagger env parsers
Close the two "[test coverage]" review gaps on PR #116 (mobile bootstrap):

- auth.controller.spec.ts: unit-test AuthController.login() returnToken
  branches via direct instantiation. returnToken:true returns exactly
  { authToken } alongside the httpOnly cookie; omitted/explicit-false return
  strictly undefined (the token must never leak into the response body for
  web clients) while the cookie is still set.
- environment.service.spec.ts: table-driven tests for getCorsAllowedOrigins()
  (split/trim/filter of CORS_ALLOWED_ORIGINS) and isSwaggerEnabled()
  (case-insensitive SWAGGER_ENABLED === 'true'), the two parsers feeding the
  CORS allowlist and Swagger exposure trust boundaries.

Tests only; no production code changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 17:05:35 +03:00

83 lines
2.7 KiB
TypeScript

import { EnvironmentService } from './environment.service';
// Direct instantiation with a stub ConfigService, mirroring the rest of these
// unit specs.
describe('EnvironmentService', () => {
let service: EnvironmentService;
beforeEach(() => {
service = new EnvironmentService(
{} as any, // configService
);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
// Fake ConfigService that honors the (key, default) signature: returns the
// mapped value when the key is present, otherwise the provided default. This
// matters because getCorsAllowedOrigins/isSwaggerEnabled call .split/.toLowerCase
// on the result, so an unset key MUST fall back to the default string.
const makeConfig = (map: Record<string, string>) =>
new EnvironmentService({
get: (key: string, def?: string) => (key in map ? map[key] : def),
} as any);
// These two parsers feed trust boundaries (CORS allowlist + Swagger exposure),
// so a parsing bug has a direct security effect. Style mirrors
// trust-proxy.util.spec.ts.
describe('EnvironmentService.getCorsAllowedOrigins', () => {
it.each([
['key unset -> empty list', {}, []],
['empty string -> empty list', { CORS_ALLOWED_ORIGINS: '' }, []],
[
'single origin',
{ CORS_ALLOWED_ORIGINS: 'https://app.example' },
['https://app.example'],
],
[
'multiple origins',
{ CORS_ALLOWED_ORIGINS: 'https://a.example,https://b.example' },
['https://a.example', 'https://b.example'],
],
[
'trims surrounding and inner spaces',
{ CORS_ALLOWED_ORIGINS: ' https://a.example , https://b.example ' },
['https://a.example', 'https://b.example'],
],
[
'double/trailing commas produce no empty strings',
{ CORS_ALLOWED_ORIGINS: 'a,, ,b' },
['a', 'b'],
],
[
'leading/trailing commas with spaces',
{ CORS_ALLOWED_ORIGINS: ' , a , , b ' },
['a', 'b'],
],
])('%s', (_label, map, expected) => {
expect(makeConfig(map as Record<string, string>).getCorsAllowedOrigins()).toEqual(
expected,
);
});
});
describe('EnvironmentService.isSwaggerEnabled', () => {
it.each([
["'true' -> true", { SWAGGER_ENABLED: 'true' }, true],
["'TRUE' -> true", { SWAGGER_ENABLED: 'TRUE' }, true],
["'True' -> true", { SWAGGER_ENABLED: 'True' }, true],
['key unset -> false', {}, false],
["'false' -> false", { SWAGGER_ENABLED: 'false' }, false],
["empty string -> false", { SWAGGER_ENABLED: '' }, false],
["'yes' -> false", { SWAGGER_ENABLED: 'yes' }, false],
["'1' -> false", { SWAGGER_ENABLED: '1' }, false],
])('%s', (_label, map, expected) => {
expect(makeConfig(map as Record<string, string>).isSwaggerEnabled()).toBe(
expected,
);
});
});