UNPKG

@prism-engineer/router

Version:

Type-safe Express.js router with automatic client generation

653 lines 25 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const createAuthScheme_1 = require("../../createAuthScheme"); const createApiRoute_1 = require("../../createApiRoute"); const typebox_1 = require("@sinclair/typebox"); (0, vitest_1.describe)('Authentication - Auth Context', () => { (0, vitest_1.beforeEach)(() => { vitest_1.vi.clearAllMocks(); }); (0, vitest_1.it)('should provide user context structure', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'user-context', validate: async () => ({ user: { id: '123', name: 'John Doe', email: 'john@example.com', role: 'admin', permissions: ['read', 'write', 'delete'] } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/user-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ userId: typebox_1.Type.String(), userName: typebox_1.Type.String() }) } }, handler: async (req) => { // TypeScript should infer the auth context type const userId = req.auth.context.user.id; const userName = req.auth.context.user.name; return { status: 200, body: { userId, userName } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide client context structure', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'client-context', validate: async () => ({ client: { id: 'client-123', name: 'Mobile App', version: '1.2.3', apiKey: 'key-456', rateLimit: 1000 } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/client-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ clientId: typebox_1.Type.String(), clientName: typebox_1.Type.String() }) } }, handler: async (req) => { const clientId = req.auth.context.client.id; const clientName = req.auth.context.client.name; return { status: 200, body: { clientId, clientName } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide service context structure', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'service-context', validate: async () => ({ service: { id: 'service-789', name: 'Background Worker', type: 'internal', permissions: ['system:read', 'system:write'], metadata: { version: '2.1.0', region: 'us-east-1' } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/service-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ serviceId: typebox_1.Type.String(), serviceType: typebox_1.Type.String() }) } }, handler: async (req) => { const serviceId = req.auth.context.service.id; const serviceType = req.auth.context.service.type; return { status: 200, body: { serviceId, serviceType } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide session context structure', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'session-context', validate: async () => ({ session: { id: 'session-abc', userId: 'user-123', expiresAt: new Date('2024-12-31T23:59:59Z'), csrfToken: 'csrf-token-xyz', metadata: { userAgent: 'Mozilla/5.0', ip: '192.168.1.1', createdAt: new Date('2024-01-01T00:00:00Z') } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/session-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ sessionId: typebox_1.Type.String(), userId: typebox_1.Type.String() }) } }, handler: async (req) => { const sessionId = req.auth.context.session.id; const userId = req.auth.context.session.userId; return { status: 200, body: { sessionId, userId } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide mixed context structure', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'mixed-context', validate: async () => ({ user: { id: 'user-123', name: 'John Doe' }, client: { id: 'client-456', name: 'Web App' }, session: { id: 'session-789', expiresAt: new Date() }, metadata: { timestamp: Date.now(), ip: '192.168.1.1', userAgent: 'Mozilla/5.0' } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/mixed-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ userId: typebox_1.Type.String(), clientId: typebox_1.Type.String(), sessionId: typebox_1.Type.String() }) } }, handler: async (req) => { const userId = req.auth.context.user.id; const clientId = req.auth.context.client.id; const sessionId = req.auth.context.session.id; return { status: 200, body: { userId, clientId, sessionId } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide typed context with interfaces', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'typed-context', validate: async () => ({ user: { id: 'user-123', email: 'john@example.com', role: 'admin', permissions: ['users:read', 'users:write'], profile: { firstName: 'John', lastName: 'Doe', avatar: 'https://example.com/avatar.jpg' } }, organizationId: 'org-456', features: ['feature-a', 'feature-b'] }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/typed-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ email: typebox_1.Type.String(), role: typebox_1.Type.String(), organizationId: typebox_1.Type.String() }) } }, handler: async (req) => { // TypeScript should provide full type checking const email = req.auth.context.user.email; const role = req.auth.context.user.role; const organizationId = req.auth.context.organizationId; return { status: 200, body: { email, role, organizationId } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with nested objects', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'nested-context', validate: async () => ({ user: { id: 'user-123', profile: { personal: { firstName: 'John', lastName: 'Doe', birthDate: '1990-01-01' }, professional: { title: 'Senior Developer', department: 'Engineering', manager: { id: 'manager-456', name: 'Jane Smith' } } }, preferences: { theme: 'dark', language: 'en', notifications: { email: true, push: false, sms: true } } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/nested-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ firstName: typebox_1.Type.String(), title: typebox_1.Type.String(), theme: typebox_1.Type.String() }) } }, handler: async (req) => { const firstName = req.auth.context.user.profile.personal.firstName; const title = req.auth.context.user.profile.professional.title; const theme = req.auth.context.user.preferences.theme; return { status: 200, body: { firstName, title, theme } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with arrays and collections', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'collections-context', validate: async () => ({ user: { id: 'user-123', roles: ['admin', 'editor'], permissions: [ { resource: 'users', actions: ['read', 'write'] }, { resource: 'posts', actions: ['read', 'write', 'delete'] } ], groups: [ { id: 'group-1', name: 'Administrators' }, { id: 'group-2', name: 'Content Editors' } ], tags: ['vip', 'beta-tester'], metadata: new Map([ ['lastLogin', '2024-01-01T12:00:00Z'], ['loginCount', 42] ]) } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/collections-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ rolesCount: typebox_1.Type.Number(), permissionsCount: typebox_1.Type.Number(), hasVipTag: typebox_1.Type.Boolean() }) } }, handler: async (req) => { const rolesCount = req.auth.context.user.roles.length; const permissionsCount = req.auth.context.user.permissions.length; const hasVipTag = req.auth.context.user.tags.includes('vip'); return { status: 200, body: { rolesCount, permissionsCount, hasVipTag } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with optional properties', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'optional-context', validate: async () => ({ user: { id: 'user-123', email: 'john@example.com', name: 'John Doe', avatar: undefined, phoneNumber: '555-1234', bio: null, settings: { theme: 'dark', language: 'en', timezone: 'UTC' } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/optional-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ hasName: typebox_1.Type.Boolean(), hasPhone: typebox_1.Type.Boolean(), language: typebox_1.Type.String() }) } }, handler: async (req) => { const hasName = !!req.auth.context.user.name; const hasPhone = !!req.auth.context.user.phoneNumber; const language = req.auth.context.user.settings.language || 'unknown'; return { status: 200, body: { hasName, hasPhone, language } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with generic types', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'generic-context', validate: async () => ({ user: { id: 'user-123', data: { preferences: { theme: 'dark', language: 'en' }, stats: { loginCount: 42, lastActive: '2024-01-01' } } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/generic-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ userId: typebox_1.Type.String(), hasData: typebox_1.Type.Boolean() }) } }, handler: async (req) => { const userId = req.auth.context.user.id; const hasData = !!req.auth.context.user.data; return { status: 200, body: { userId, hasData } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with computed properties', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'computed-context', validate: async () => { const baseUser = { id: 'user-123', firstName: 'John', lastName: 'Doe', permissions: ['read', 'write'] }; return { user: { ...baseUser, get fullName() { return `${baseUser.firstName} ${baseUser.lastName}`; }, get isAdmin() { return baseUser.permissions.includes('admin'); }, get canWrite() { return baseUser.permissions.includes('write'); } } }; } }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/computed-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ fullName: typebox_1.Type.String(), canWrite: typebox_1.Type.Boolean() }) } }, handler: async (req) => { const fullName = req.auth.context.user.fullName; const canWrite = req.auth.context.user.canWrite; return { status: 200, body: { fullName, canWrite } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with validation metadata', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'validation-metadata', validate: async (req) => ({ user: { id: 'user-123', email: 'john@example.com' }, validation: { method: 'bearer-token', timestamp: Date.now(), ip: req.ip || 'unknown', userAgent: req.headers['user-agent'] || 'unknown', secure: req.headers['x-forwarded-proto'] === 'https', source: 'api-gateway' } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/validation-metadata', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ userId: typebox_1.Type.String(), validationMethod: typebox_1.Type.String(), isSecure: typebox_1.Type.Boolean() }) } }, handler: async (req) => { const userId = req.auth.context.user.id; const validationMethod = req.auth.context.validation.method; const isSecure = req.auth.context.validation.secure; return { status: 200, body: { userId, validationMethod, isSecure } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should provide context with organization hierarchy', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'organization-context', validate: async () => ({ user: { id: 'user-123', email: 'john@acme.com' }, organization: { id: 'org-456', name: 'ACME Corp', tier: 'enterprise', features: ['sso', 'audit-logs', 'custom-roles'], hierarchy: { department: { id: 'dept-789', name: 'Engineering' }, team: { id: 'team-012', name: 'Backend Team' }, role: { id: 'role-345', name: 'Senior Developer', level: 5 } } } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/organization-context', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ orgName: typebox_1.Type.String(), department: typebox_1.Type.String(), roleLevel: typebox_1.Type.Number() }) } }, handler: async (req) => { const orgName = req.auth.context.organization.name; const department = req.auth.context.organization.hierarchy.department.name; const roleLevel = req.auth.context.organization.hierarchy.role.level; return { status: 200, body: { orgName, department, roleLevel } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); (0, vitest_1.it)('should handle context with type guards', () => { const authScheme = (0, createAuthScheme_1.createAuthScheme)({ name: 'type-guards', validate: async () => ({ entity: { type: 'user', id: 'user-123', name: 'John Doe' } }) }); const route = (0, createApiRoute_1.createApiRoute)({ path: '/api/type-guards', method: 'GET', auth: authScheme, response: { 200: { contentType: 'application/json', body: typebox_1.Type.Object({ entityType: typebox_1.Type.String(), entityName: typebox_1.Type.String() }) } }, handler: async (req) => { const entity = req.auth.context.entity; // Type guard usage if (entity.type === 'user') { const entityType = entity.type; const entityName = entity.name; return { status: 200, body: { entityType, entityName } }; } return { status: 200, body: { entityType: 'unknown', entityName: 'unknown' } }; } }); (0, vitest_1.expect)(route.auth).toBe(authScheme); }); }); //# sourceMappingURL=auth-context.test.js.map