@prism-engineer/router
Version:
Type-safe Express.js router with automatic client generation
653 lines • 25 kB
JavaScript
"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