Files
TREK/server/tests/unit/nest/oidc.service.test.ts
T
Maurice 7266ad99ae Restore nest coverage to >=80% after the #1209 dep bump (istanbul provider + branch tests) (#1213)
* fix(server): set oxc:false in vitest so the SWC transform survives the Vite 8 bump

* fix(server): switch coverage to the istanbul provider (v8 under-reports branches on Vite 8 + Vitest 4)

* test(nest): cover controller/service branches to clear the 80% coverage gate
2026-06-16 21:36:39 +02:00

159 lines
6.1 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Request, Response } from 'express';
// The Nest service is a thin pass-through to the legacy OIDC helpers plus a few
// adjacent service modules. Mock each one and assert the wrapper forwards every
// argument and returns whatever the legacy function hands back.
const { oidc } = vi.hoisted(() => ({
oidc: {
getOidcConfig: vi.fn(),
discover: vi.fn(),
createState: vi.fn(),
consumeState: vi.fn(),
exchangeCodeForToken: vi.fn(),
verifyIdToken: vi.fn(),
getUserInfo: vi.fn(),
findOrCreateUser: vi.fn(),
touchLastLogin: vi.fn(),
generateToken: vi.fn(),
createAuthCode: vi.fn(),
consumeAuthCode: vi.fn(),
frontendUrl: vi.fn(),
},
}));
vi.mock('../../../src/services/oidcService', () => oidc);
const { getAppUrl } = vi.hoisted(() => ({ getAppUrl: vi.fn() }));
vi.mock('../../../src/services/notifications', () => ({ getAppUrl }));
const { resolveAuthToggles } = vi.hoisted(() => ({ resolveAuthToggles: vi.fn() }));
vi.mock('../../../src/services/authService', () => ({ resolveAuthToggles }));
const { setAuthCookie } = vi.hoisted(() => ({ setAuthCookie: vi.fn() }));
vi.mock('../../../src/services/cookie', () => ({ setAuthCookie }));
import { OidcService } from '../../../src/nest/oidc/oidc.service';
let s: OidcService;
beforeEach(() => {
vi.clearAllMocks();
s = new OidcService();
});
describe('OidcService', () => {
it('oidcLoginEnabled reads the resolved auth toggle', () => {
resolveAuthToggles.mockReturnValue({ oidc_login: true });
expect(s.oidcLoginEnabled()).toBe(true);
resolveAuthToggles.mockReturnValue({ oidc_login: false });
expect(s.oidcLoginEnabled()).toBe(false);
});
it('getOidcConfig delegates to the legacy helper', () => {
const cfg = { issuer: 'https://idp' };
oidc.getOidcConfig.mockReturnValue(cfg);
expect(s.getOidcConfig()).toBe(cfg);
});
it('getAppUrl delegates to notifications.getAppUrl', () => {
getAppUrl.mockReturnValue('https://app');
expect(s.getAppUrl()).toBe('https://app');
});
it('discover forwards the issuer and discovery url', () => {
const doc = { authorization_endpoint: 'https://idp/auth' };
oidc.discover.mockReturnValue(doc);
expect(s.discover('https://idp', 'https://idp/.well-known')).toBe(doc);
expect(oidc.discover).toHaveBeenCalledWith('https://idp', 'https://idp/.well-known');
});
it('discover works without a discovery url', () => {
oidc.discover.mockReturnValue('doc');
expect(s.discover('https://idp')).toBe('doc');
expect(oidc.discover).toHaveBeenCalledWith('https://idp', undefined);
});
it('createState forwards the redirect uri and invite token', () => {
const st = { state: 'st', codeChallenge: 'cc' };
oidc.createState.mockReturnValue(st);
expect(s.createState('https://app/cb', 'inv')).toBe(st);
expect(oidc.createState).toHaveBeenCalledWith('https://app/cb', 'inv');
});
it('createState works without an invite token', () => {
oidc.createState.mockReturnValue({ state: 'st', codeChallenge: 'cc' });
s.createState('https://app/cb');
expect(oidc.createState).toHaveBeenCalledWith('https://app/cb', undefined);
});
it('consumeState forwards the state', () => {
oidc.consumeState.mockReturnValue({ redirectUri: 'r', codeVerifier: 'v' });
expect(s.consumeState('st')).toEqual({ redirectUri: 'r', codeVerifier: 'v' });
expect(oidc.consumeState).toHaveBeenCalledWith('st');
});
it('exchangeCodeForToken spreads all arguments through', () => {
oidc.exchangeCodeForToken.mockReturnValue({ _ok: true });
const doc = { token_endpoint: 'https://idp/token' } as never;
expect(s.exchangeCodeForToken(doc, 'code', 'redir', 'cid', 'secret', 'verifier')).toEqual({ _ok: true });
expect(oidc.exchangeCodeForToken).toHaveBeenCalledWith(doc, 'code', 'redir', 'cid', 'secret', 'verifier');
});
it('verifyIdToken spreads all arguments through', () => {
oidc.verifyIdToken.mockReturnValue({ ok: true });
const doc = { issuer: 'https://idp' } as never;
expect(s.verifyIdToken('id_token', doc, 'cid', 'https://idp')).toEqual({ ok: true });
expect(oidc.verifyIdToken).toHaveBeenCalledWith('id_token', doc, 'cid', 'https://idp');
});
it('getUserInfo forwards the endpoint and access token', () => {
oidc.getUserInfo.mockReturnValue({ email: 'a@b.c' });
expect(s.getUserInfo('https://idp/ui', 'at')).toEqual({ email: 'a@b.c' });
expect(oidc.getUserInfo).toHaveBeenCalledWith('https://idp/ui', 'at');
});
it('findOrCreateUser spreads all arguments through', () => {
const result = { user: { id: 1 } };
oidc.findOrCreateUser.mockReturnValue(result);
const info = { email: 'a@b.c' } as never;
const cfg = { issuer: 'https://idp' } as never;
expect(s.findOrCreateUser(info, cfg, 'inv')).toBe(result);
expect(oidc.findOrCreateUser).toHaveBeenCalledWith(info, cfg, 'inv');
});
it('touchLastLogin forwards the user id', () => {
s.touchLastLogin(42);
expect(oidc.touchLastLogin).toHaveBeenCalledWith(42);
});
it('generateToken forwards the user', () => {
oidc.generateToken.mockReturnValue('jwt');
expect(s.generateToken({ id: 7 })).toBe('jwt');
expect(oidc.generateToken).toHaveBeenCalledWith({ id: 7 });
});
it('createAuthCode forwards the token', () => {
oidc.createAuthCode.mockReturnValue('ac');
expect(s.createAuthCode('jwt')).toBe('ac');
expect(oidc.createAuthCode).toHaveBeenCalledWith('jwt');
});
it('consumeAuthCode forwards the code', () => {
oidc.consumeAuthCode.mockReturnValue({ token: 'jwt' });
expect(s.consumeAuthCode('ac')).toEqual({ token: 'jwt' });
expect(oidc.consumeAuthCode).toHaveBeenCalledWith('ac');
});
it('frontendUrl forwards the path', () => {
oidc.frontendUrl.mockReturnValue('https://app/login');
expect(s.frontendUrl('/login')).toBe('https://app/login');
expect(oidc.frontendUrl).toHaveBeenCalledWith('/login');
});
it('setAuthCookie forwards res, token and req to the cookie helper', () => {
const res = {} as Response;
const req = {} as Request;
s.setAuthCookie(res, 'jwt', req);
expect(setAuthCookie).toHaveBeenCalledWith(res, 'jwt', req);
});
});