Skip to content

Commit bbfbbe5

Browse files
committed
feat: 30% test coverage
1 parent ededb0b commit bbfbbe5

14 files changed

Lines changed: 1159 additions & 19 deletions

File tree

src/controllers/jwks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ let jwkCache: JwkCache | null = null;
2020

2121
const CACHE_TTL = 1000 * 60 * 5;
2222

23+
export function __resetJwksCache() {
24+
jwkCache = null;
25+
}
26+
2327
async function loadJwksFromSecrets(): Promise<JWK[]> {
2428
logger.info('Loading JWKS from Secrets Manager');
2529

src/routes/users.routes.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ usersRouter.post(
3232
tags: ['Users'],
3333
summary: 'Update credential metadata',
3434

35-
middleware: [attachAuthMiddleware],
35+
middleware: [attachAuthMiddleware('access')],
3636

3737
schemas: {
3838
body: UpdateCredentialRequestSchema,
@@ -48,7 +48,7 @@ usersRouter.delete(
4848
tags: ['Users'],
4949
summary: 'Delete authenticated user',
5050

51-
middleware: [attachAuthMiddleware],
51+
middleware: [attachAuthMiddleware('access')],
5252

5353
schemas: {
5454
response: MessageSchema,
@@ -64,7 +64,7 @@ usersRouter.delete(
6464
tags: ['Users'],
6565
summary: 'Delete credential',
6666

67-
middleware: [attachAuthMiddleware],
67+
middleware: [attachAuthMiddleware('access')],
6868

6969
schemas: {
7070
body: DeleteCredentialRequestSchema,

tests/e2e/authFlow.spec.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import request from 'supertest';
2+
import { describe, it, expect, beforeAll, beforeEach, vi } from 'vitest';
3+
import { createApp } from '../../src/app';
4+
import { Application } from 'express';
5+
6+
vi.mock('../../src/config/getSystemConfig.js', () => ({
7+
getSystemConfig: vi.fn(),
8+
}));
9+
10+
vi.mock('bcrypt-ts', () => ({
11+
compareSync: vi.fn(),
12+
}));
13+
14+
vi.mock('../../../src/services/authEventService.js', () => ({
15+
AuthEventService: {
16+
log: vi.fn(),
17+
notificationSent: vi.fn(),
18+
},
19+
}));
20+
21+
import { User } from '../../src/models/users.js';
22+
import { Session } from '../../src/models/sessions.js';
23+
24+
import {
25+
signEphemeralToken,
26+
signAccessToken,
27+
generateRefreshToken,
28+
hashRefreshToken,
29+
} from '../../src/lib/token.js';
30+
31+
import { generatePhoneOTP, verifyPhoneOTP } from '../../src/utils/otp.js';
32+
33+
import { validateAccessToken } from '../../src/services/sessionService.js';
34+
35+
import { getSystemConfig } from '../../src/config/getSystemConfig.js';
36+
37+
import { compareSync } from 'bcrypt-ts';
38+
import { buildRegistrationRequest } from '../factories/requestFactory.js';
39+
import { buildUser } from '../factories/userFactory.js';
40+
import { buildSession } from '../factories/sessionFactory.js';
41+
42+
let app: Application;
43+
44+
beforeAll(async () => {
45+
app = await createApp();
46+
});
47+
48+
beforeEach(() => {
49+
vi.clearAllMocks();
50+
51+
(getSystemConfig as any).mockResolvedValue({
52+
default_roles: ['user'],
53+
});
54+
55+
(signEphemeralToken as any).mockResolvedValue('ephemeral-token');
56+
(signAccessToken as any).mockResolvedValue('access-token');
57+
(generateRefreshToken as any).mockReturnValue('refresh-token');
58+
(hashRefreshToken as any).mockResolvedValue('hashed-refresh');
59+
(Session.create as any).mockResolvedValue({ id: 'session-1' });
60+
61+
(compareSync as any).mockReturnValue(true);
62+
});
63+
64+
describe('E2E Auth Flow', () => {
65+
it('completes full auth lifecycle', async () => {
66+
(User.findOne as any).mockResolvedValue(null);
67+
68+
(User.create as any).mockResolvedValue(buildUser());
69+
70+
const registerRes = await request(app)
71+
.post('/registration/register')
72+
.send(buildRegistrationRequest());
73+
74+
expect(registerRes.status).toBe(200);
75+
76+
const otpRes = await request(app)
77+
.get('/otp/generate-phone-otp')
78+
.set('Cookie', [`seamless_ephemeral=ephemeral-token`]);
79+
80+
expect(otpRes.status).toBe(200);
81+
expect(generatePhoneOTP).toHaveBeenCalled();
82+
83+
(verifyPhoneOTP as any).mockResolvedValue({
84+
user: {
85+
id: 'user-1',
86+
emailVerified: true,
87+
phoneVerified: true,
88+
verified: true,
89+
roles: ['user'],
90+
},
91+
verified: true,
92+
});
93+
94+
(Session.create as any).mockResolvedValue({
95+
id: 'session-1',
96+
});
97+
98+
const verifyRes = await request(app)
99+
.post('/otp/verify-phone-otp')
100+
.set('Cookie', [`seamless_ephemeral=ephemeral-token`])
101+
.send({ verificationToken: '123456' });
102+
103+
expect(verifyRes.status).toBe(200);
104+
105+
(validateAccessToken as any).mockResolvedValue({
106+
sessionId: 'session-1',
107+
});
108+
109+
(Session.findAll as any).mockResolvedValue([buildSession()]);
110+
const accessRes = await request(app)
111+
.get('/sessions')
112+
.set('Cookie', [`seamless_access=access-token`]);
113+
114+
expect(accessRes.status).toBe(200);
115+
116+
(validateAccessToken as any).mockResolvedValue(null);
117+
118+
(User.findByPk as any).mockResolvedValue({
119+
id: 'user-1',
120+
});
121+
122+
(Session.create as any).mockResolvedValue({
123+
id: 'session-2',
124+
});
125+
126+
const refreshRes = await request(app)
127+
.get('/sessions')
128+
.set('Cookie', [`seamless_refresh=refresh-token`]);
129+
130+
expect(refreshRes.status).toBe(200);
131+
});
132+
});

tests/factories/sessionFactory.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export function buildSession(overrides: any = {}) {
2+
return {
3+
id: 'session-1',
4+
deviceName: 'MacBook',
5+
ipAddress: '127.0.0.1',
6+
userAgent: 'agent',
7+
lastUsedAt: new Date(),
8+
expiresAt: new Date(Date.now() + 100000),
9+
revokedAt: null,
10+
...overrides,
11+
};
12+
}

0 commit comments

Comments
 (0)