Implement medicine library and cabinet
This commit is contained in:
parent
db79af06f7
commit
1f66fab30f
72 changed files with 7642 additions and 319 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import Fastify from 'fastify';
|
||||
import { fastifyAwilixPlugin } from '@fastify/awilix';
|
||||
import { asValue } from 'awilix';
|
||||
|
||||
const { MockJOSEError } = vi.hoisted(() => ({
|
||||
MockJOSEError: class JOSEError extends Error {},
|
||||
|
|
@ -12,21 +14,38 @@ vi.mock('jose', () => ({
|
|||
errors: { JOSEError: MockJOSEError },
|
||||
}));
|
||||
|
||||
const { mockFindByKeycloakId, mockUpsertFromToken } = vi.hoisted(() => ({
|
||||
mockFindByKeycloakId: vi.fn(),
|
||||
mockUpsertFromToken: vi.fn(),
|
||||
}));
|
||||
|
||||
import authPlugin from './auth.plugin.js';
|
||||
import * as jose from 'jose';
|
||||
|
||||
describe('auth.plugin', () => {
|
||||
function buildApp() {
|
||||
async function buildApp() {
|
||||
const app = Fastify({ logger: false });
|
||||
await app.register(fastifyAwilixPlugin, {
|
||||
disposeOnClose: true,
|
||||
disposeOnResponse: true,
|
||||
strictBooleanEnforced: true,
|
||||
});
|
||||
app.diContainer.register({
|
||||
usersRepository: asValue({
|
||||
findByKeycloakId: mockFindByKeycloakId,
|
||||
upsertFromToken: mockUpsertFromToken,
|
||||
}),
|
||||
});
|
||||
return app;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockFindByKeycloakId.mockResolvedValue({ householdIds: ['hh1'] });
|
||||
});
|
||||
|
||||
it('skips auth for routes marked as public', async () => {
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
app.get('/public', { config: { public: true } as never }, async () => ({ ok: true }));
|
||||
|
|
@ -37,7 +56,7 @@ describe('auth.plugin', () => {
|
|||
});
|
||||
|
||||
it('throws 401 when no Authorization header', async () => {
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
app.get('/protected', async () => ({ ok: true }));
|
||||
|
|
@ -49,7 +68,7 @@ describe('auth.plugin', () => {
|
|||
});
|
||||
|
||||
it('throws 401 when Authorization header is not Bearer', async () => {
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
app.get('/protected', async () => ({ ok: true }));
|
||||
|
|
@ -66,7 +85,7 @@ describe('auth.plugin', () => {
|
|||
it('throws 401 when token is invalid', async () => {
|
||||
vi.mocked(jose.jwtVerify).mockRejectedValue(new MockJOSEError('Invalid token'));
|
||||
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
app.get('/protected', async () => ({ ok: true }));
|
||||
|
|
@ -88,15 +107,15 @@ describe('auth.plugin', () => {
|
|||
email: 'test@example.com',
|
||||
preferred_username: 'testuser',
|
||||
realm_access: { roles: ['member'] },
|
||||
householdIds: ['hh1'],
|
||||
iss: 'http://localhost:8080/realms/meshitrack',
|
||||
aud: 'meshitrack-api',
|
||||
},
|
||||
protectedHeader: { alg: 'RS256' },
|
||||
key: {} as never,
|
||||
} as never);
|
||||
mockFindByKeycloakId.mockResolvedValue({ householdIds: ['hh1'] });
|
||||
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
let capturedUser: unknown;
|
||||
|
|
@ -122,18 +141,37 @@ describe('auth.plugin', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('rethrows non-JOSE errors as-is', async () => {
|
||||
vi.mocked(jose.jwtVerify).mockRejectedValue(new Error('Network failure'));
|
||||
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
app.get('/protected', async () => ({ ok: true }));
|
||||
await app.ready();
|
||||
|
||||
const res = await app.inject({
|
||||
method: 'GET',
|
||||
url: '/protected',
|
||||
headers: { authorization: 'Bearer some-token' },
|
||||
});
|
||||
expect(res.statusCode).toBe(500);
|
||||
});
|
||||
|
||||
it('handles missing optional fields in JWT payload', async () => {
|
||||
vi.mocked(jose.jwtVerify).mockResolvedValue({
|
||||
payload: {
|
||||
// sub, email, preferred_username, realm_access, householdIds all missing
|
||||
// sub, email, preferred_username, realm_access all missing
|
||||
iss: 'http://localhost:8080/realms/meshitrack',
|
||||
aud: 'meshitrack-api',
|
||||
},
|
||||
protectedHeader: { alg: 'RS256' },
|
||||
key: {} as never,
|
||||
} as never);
|
||||
mockFindByKeycloakId.mockResolvedValue(null);
|
||||
mockUpsertFromToken.mockResolvedValue({ householdIds: [] });
|
||||
|
||||
const app = buildApp();
|
||||
const app = await buildApp();
|
||||
await app.register(authPlugin);
|
||||
|
||||
let capturedUser: unknown;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue