import { Request, Response } from 'express'; import { DyFM_Error } from '@futdevpro/fsm-dynamo'; import { DyNTS_AdminApiKey_AuthService } from './admin-api-key.auth-service'; const TEST_KEY: string = 'super-secret-admin-key-1234567890'; const TEST_ENV_VAR: string = 'DYNTS_ADMIN_API_KEY'; /** Test-only — minimal Request mock-ot ad vissza adott headers-szel. */ const mockReq = (headers: Record = {}): Request => { return { headers: headers } as unknown as Request; }; /** Test-only — minimal Response stub (verify nem irja, csak typing miatt kell). */ const mockRes = (): Response => { return {} as unknown as Response; }; describe('| DyNTS_AdminApiKey_AuthService', (): void => { let svc: DyNTS_AdminApiKey_AuthService; let originalEnv: string | undefined; beforeEach((): void => { svc = DyNTS_AdminApiKey_AuthService.getInstance(); svc._resetForTesting(); originalEnv = process.env[TEST_ENV_VAR]; }); afterEach((): void => { svc._resetForTesting(); if (originalEnv === undefined) { delete process.env[TEST_ENV_VAR]; } else { process.env[TEST_ENV_VAR] = originalEnv; } }); describe('| verify() — config errors', (): void => { it('| 500 ha az env var nincs beallitva', async (): Promise => { delete process.env[TEST_ENV_VAR]; let thrown: any = null; try { await svc.verify(mockReq({}), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(500); expect(DyFM_Error.getErrorCode(thrown)).toContain('DyNTS-AAK-CONFIG'); }); it('| 500 ha az env var ures string', async (): Promise => { process.env[TEST_ENV_VAR] = ''; let thrown: any = null; try { await svc.verify(mockReq({}), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(500); }); }); describe('| verify() — auth errors', (): void => { beforeEach((): void => { process.env[TEST_ENV_VAR] = TEST_KEY; }); it('| 401 ha a header teljesen hianyzik', async (): Promise => { let thrown: any = null; try { await svc.verify(mockReq({}), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(401); expect(DyFM_Error.getErrorCode(thrown)).toContain('DyNTS-AAK-MISSING'); }); it('| 401 ha a header rossz erteku', async (): Promise => { let thrown: any = null; try { await svc.verify(mockReq({ 'x-admin-api-key': 'wrong-key' }), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(401); expect(DyFM_Error.getErrorCode(thrown)).toContain('DyNTS-AAK-INVALID'); }); it('| 401 length-mismatch eseten is (timing-safe path)', async (): Promise => { let thrown: any = null; try { await svc.verify(mockReq({ 'x-admin-api-key': 'short' }), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(401); expect(DyFM_Error.getErrorCode(thrown)).toContain('DyNTS-AAK-INVALID'); }); }); describe('| verify() — happy paths', (): void => { beforeEach((): void => { process.env[TEST_ENV_VAR] = TEST_KEY; }); it('| silent pass ha az x-admin-api-key header egyezik', async (): Promise => { await expectAsync( svc.verify(mockReq({ 'x-admin-api-key': TEST_KEY }), mockRes()), ).toBeResolved(); }); it('| silent pass Authorization Bearer fallback-bol (default engedelyezve)', async (): Promise => { await expectAsync( svc.verify(mockReq({ authorization: `Bearer ${TEST_KEY}` }), mockRes()), ).toBeResolved(); }); it('| silent pass Authorization Bearer kis-nagybetu case-insensitive', async (): Promise => { await expectAsync( svc.verify(mockReq({ authorization: `bearer ${TEST_KEY}` }), mockRes()), ).toBeResolved(); }); }); describe('| configure() — overrides', (): void => { it('| custom envVarName-bol olvas', async (): Promise => { svc.configure({ envVarName: 'MY_CUSTOM_KEY' }); process.env['MY_CUSTOM_KEY'] = TEST_KEY; await expectAsync( svc.verify(mockReq({ 'x-admin-api-key': TEST_KEY }), mockRes()), ).toBeResolved(); delete process.env['MY_CUSTOM_KEY']; }); it('| custom headerName-rol olvas', async (): Promise => { process.env[TEST_ENV_VAR] = TEST_KEY; svc.configure({ headerName: 'x-my-admin' }); // Default x-admin-api-key MOSTANTOL NEM mukodik let thrown: any = null; try { await svc.verify(mockReq({ 'x-admin-api-key': TEST_KEY }), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(401); // Az uj header viszont igen await expectAsync( svc.verify(mockReq({ 'x-my-admin': TEST_KEY }), mockRes()), ).toBeResolved(); }); it('| allowAuthorizationBearer=false letiltja a Bearer fallback-et', async (): Promise => { process.env[TEST_ENV_VAR] = TEST_KEY; svc.configure({ allowAuthorizationBearer: false }); let thrown: any = null; try { await svc.verify(mockReq({ authorization: `Bearer ${TEST_KEY}` }), mockRes()); } catch (e) { thrown = e; } expect(thrown).not.toBeNull(); expect(DyFM_Error.getErrorStatus(thrown)).toBe(401); }); it('| configure() partial override — a tobbi mezo default marad', (): void => { svc.configure({ headerName: 'x-only-header' }); const config = svc.getConfig(); expect(config.headerName).toBe('x-only-header'); expect(config.envVarName).toBe('DYNTS_ADMIN_API_KEY'); expect(config.allowAuthorizationBearer).toBe(true); }); it('| configure() headerName lowercase-re normalizal', (): void => { svc.configure({ headerName: 'X-Mixed-Case-Header' }); expect(svc.getConfig().headerName).toBe('x-mixed-case-header'); }); }); describe('| env-read-on-each-call (dynamic)', (): void => { it('| env var update kozben hat: install-utani env-modositas ervenyes', async (): Promise => { delete process.env[TEST_ENV_VAR]; // Elso hivas: env hianyzik → 500 let thrown: any = null; try { await svc.verify(mockReq({}), mockRes()); } catch (e) { thrown = e; } expect(DyFM_Error.getErrorStatus(thrown)).toBe(500); // Most allitsuk be az env-et process.env[TEST_ENV_VAR] = TEST_KEY; // Masodik hivas helyes header-rel → pass await expectAsync( svc.verify(mockReq({ 'x-admin-api-key': TEST_KEY }), mockRes()), ).toBeResolved(); }); }); describe('| .verify binding', (): void => { it('| verify atadhato preProcesses-be detached method-kent (this-binding megtarttva)', async (): Promise => { process.env[TEST_ENV_VAR] = TEST_KEY; const detached: (req: Request, res: Response) => Promise = svc.verify; // NEM call-ban svc.verify(...) hanem szabad fuggveny-kent — bindelt this-re kell hagyatkozni await expectAsync( detached(mockReq({ 'x-admin-api-key': TEST_KEY }), mockRes()), ).toBeResolved(); }); }); });