1113 lines
45 KiB
TypeScript
1113 lines
45 KiB
TypeScript
/**
|
|
* Contract Validation Tests for Auth Module
|
|
*
|
|
* These tests validate that the auth API DTOs and OpenAPI spec are consistent
|
|
* and that the generated types will be compatible with the website auth client.
|
|
*/
|
|
|
|
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
interface OpenAPISchema {
|
|
type?: string;
|
|
format?: string;
|
|
$ref?: string;
|
|
items?: OpenAPISchema;
|
|
properties?: Record<string, OpenAPISchema>;
|
|
required?: string[];
|
|
enum?: string[];
|
|
nullable?: boolean;
|
|
description?: string;
|
|
default?: unknown;
|
|
}
|
|
|
|
interface OpenAPISpec {
|
|
openapi: string;
|
|
info: {
|
|
title: string;
|
|
description: string;
|
|
version: string;
|
|
};
|
|
paths: Record<string, any>;
|
|
components: {
|
|
schemas: Record<string, OpenAPISchema>;
|
|
};
|
|
}
|
|
|
|
describe('Auth Module Contract Validation', () => {
|
|
const apiRoot = path.join(__dirname, '../..');
|
|
const openapiPath = path.join(apiRoot, 'apps/api/openapi.json');
|
|
const generatedTypesDir = path.join(apiRoot, 'apps/website/lib/types/generated');
|
|
const websiteTypesDir = path.join(apiRoot, 'apps/website/lib/types');
|
|
|
|
describe('OpenAPI Spec Integrity for Auth Endpoints', () => {
|
|
it('should have auth endpoints defined in OpenAPI spec', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Check for auth endpoints
|
|
expect(spec.paths['/auth/signup']).toBeDefined();
|
|
expect(spec.paths['/auth/login']).toBeDefined();
|
|
expect(spec.paths['/auth/session']).toBeDefined();
|
|
expect(spec.paths['/auth/logout']).toBeDefined();
|
|
expect(spec.paths['/auth/forgot-password']).toBeDefined();
|
|
expect(spec.paths['/auth/reset-password']).toBeDefined();
|
|
|
|
// Verify POST methods exist for signup, login, forgot-password, reset-password
|
|
expect(spec.paths['/auth/signup'].post).toBeDefined();
|
|
expect(spec.paths['/auth/login'].post).toBeDefined();
|
|
expect(spec.paths['/auth/forgot-password'].post).toBeDefined();
|
|
expect(spec.paths['/auth/reset-password'].post).toBeDefined();
|
|
|
|
// Verify GET methods exist for session
|
|
expect(spec.paths['/auth/session'].get).toBeDefined();
|
|
});
|
|
|
|
it('should have AuthSessionDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['AuthSessionDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('token');
|
|
expect(schema.required).toContain('user');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.token?.type).toBe('string');
|
|
expect(schema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
});
|
|
|
|
it('should have AuthenticatedUserDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['AuthenticatedUserDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('userId');
|
|
expect(schema.required).toContain('email');
|
|
expect(schema.required).toContain('displayName');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.userId?.type).toBe('string');
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
expect(schema.properties?.displayName?.type).toBe('string');
|
|
|
|
// Verify optional fields
|
|
expect(schema.properties?.primaryDriverId).toBeDefined();
|
|
expect(schema.properties?.avatarUrl).toBeDefined();
|
|
expect(schema.properties?.avatarUrl?.nullable).toBe(true);
|
|
expect(schema.properties?.companyId).toBeDefined();
|
|
expect(schema.properties?.role).toBeDefined();
|
|
});
|
|
|
|
it('should have SignupParamsDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['SignupParamsDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('email');
|
|
expect(schema.required).toContain('password');
|
|
expect(schema.required).toContain('displayName');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
expect(schema.properties?.password?.type).toBe('string');
|
|
expect(schema.properties?.displayName?.type).toBe('string');
|
|
|
|
// Verify optional fields
|
|
expect(schema.properties?.username).toBeDefined();
|
|
expect(schema.properties?.iracingCustomerId).toBeDefined();
|
|
expect(schema.properties?.primaryDriverId).toBeDefined();
|
|
expect(schema.properties?.avatarUrl).toBeDefined();
|
|
expect(schema.properties?.avatarUrl?.nullable).toBe(true);
|
|
});
|
|
|
|
it('should have LoginParamsDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['LoginParamsDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('email');
|
|
expect(schema.required).toContain('password');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
expect(schema.properties?.password?.type).toBe('string');
|
|
|
|
// Verify optional fields
|
|
expect(schema.properties?.rememberMe).toBeDefined();
|
|
expect(schema.properties?.rememberMe?.type).toBe('boolean');
|
|
expect(schema.properties?.rememberMe?.default).toBe(false);
|
|
});
|
|
|
|
it('should have ForgotPasswordDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['ForgotPasswordDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('email');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
});
|
|
|
|
it('should have ResetPasswordDTO schema defined', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['ResetPasswordDTO'];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// Verify required fields
|
|
expect(schema.required).toContain('token');
|
|
expect(schema.required).toContain('newPassword');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.token?.type).toBe('string');
|
|
expect(schema.properties?.newPassword?.type).toBe('string');
|
|
});
|
|
|
|
it('should have proper request/response structure for signup endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const signupPath = spec.paths['/auth/signup']?.post;
|
|
expect(signupPath).toBeDefined();
|
|
|
|
// Verify request body
|
|
const requestBody = signupPath.requestBody;
|
|
expect(requestBody).toBeDefined();
|
|
expect(requestBody.content['application/json']).toBeDefined();
|
|
expect(requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/SignupParamsDTO');
|
|
|
|
// Verify response
|
|
const response200 = signupPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
expect(response200.content['application/json'].schema.$ref).toBe('#/components/schemas/AuthSessionDTO');
|
|
});
|
|
|
|
it('should have proper request/response structure for login endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const loginPath = spec.paths['/auth/login']?.post;
|
|
expect(loginPath).toBeDefined();
|
|
|
|
// Verify request body
|
|
const requestBody = loginPath.requestBody;
|
|
expect(requestBody).toBeDefined();
|
|
expect(requestBody.content['application/json']).toBeDefined();
|
|
expect(requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/LoginParamsDTO');
|
|
|
|
// Verify response
|
|
const response200 = loginPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
expect(response200.content['application/json'].schema.$ref).toBe('#/components/schemas/AuthSessionDTO');
|
|
});
|
|
|
|
it('should have proper response structure for session endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const sessionPath = spec.paths['/auth/session']?.get;
|
|
expect(sessionPath).toBeDefined();
|
|
|
|
// Verify response
|
|
const response200 = sessionPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
expect(response200.content['application/json'].schema.$ref).toBe('#/components/schemas/AuthSessionDTO');
|
|
});
|
|
|
|
it('should have proper request/response structure for forgot-password endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const forgotPasswordPath = spec.paths['/auth/forgot-password']?.post;
|
|
expect(forgotPasswordPath).toBeDefined();
|
|
|
|
// Verify request body
|
|
const requestBody = forgotPasswordPath.requestBody;
|
|
expect(requestBody).toBeDefined();
|
|
expect(requestBody.content['application/json']).toBeDefined();
|
|
expect(requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/ForgotPasswordDTO');
|
|
|
|
// Verify response
|
|
const response200 = forgotPasswordPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
});
|
|
|
|
it('should have proper request/response structure for reset-password endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const resetPasswordPath = spec.paths['/auth/reset-password']?.post;
|
|
expect(resetPasswordPath).toBeDefined();
|
|
|
|
// Verify request body
|
|
const requestBody = resetPasswordPath.requestBody;
|
|
expect(requestBody).toBeDefined();
|
|
expect(requestBody.content['application/json']).toBeDefined();
|
|
expect(requestBody.content['application/json'].schema.$ref).toBe('#/components/schemas/ResetPasswordDTO');
|
|
|
|
// Verify response
|
|
const response200 = resetPasswordPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('DTO Consistency', () => {
|
|
it('should have generated DTO files for auth schemas', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const generatedFiles = await fs.readdir(generatedTypesDir);
|
|
const generatedDTOs = generatedFiles
|
|
.filter(f => f.endsWith('.ts'))
|
|
.map(f => f.replace('.ts', ''));
|
|
|
|
// Check for auth-related DTOs
|
|
const authDTOs = [
|
|
'AuthSessionDTO',
|
|
'AuthenticatedUserDTO',
|
|
'SignupParamsDTO',
|
|
'LoginParamsDTO',
|
|
'ForgotPasswordDTO',
|
|
'ResetPasswordDTO',
|
|
];
|
|
|
|
for (const dtoName of authDTOs) {
|
|
expect(spec.components.schemas[dtoName]).toBeDefined();
|
|
expect(generatedDTOs).toContain(dtoName);
|
|
}
|
|
});
|
|
|
|
it('should have consistent property types between DTOs and schemas', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
const schemas = spec.components.schemas;
|
|
|
|
// Test AuthSessionDTO
|
|
const authSessionSchema = schemas['AuthSessionDTO'];
|
|
const authSessionDtoPath = path.join(generatedTypesDir, 'AuthSessionDTO.ts');
|
|
const authSessionDtoExists = await fs.access(authSessionDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (authSessionDtoExists) {
|
|
const authSessionDtoContent = await fs.readFile(authSessionDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (authSessionSchema.required) {
|
|
for (const requiredProp of authSessionSchema.required) {
|
|
expect(authSessionDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (authSessionSchema.properties) {
|
|
for (const propName of Object.keys(authSessionSchema.properties)) {
|
|
expect(authSessionDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test AuthenticatedUserDTO
|
|
const authenticatedUserSchema = schemas['AuthenticatedUserDTO'];
|
|
const authenticatedUserDtoPath = path.join(generatedTypesDir, 'AuthenticatedUserDTO.ts');
|
|
const authenticatedUserDtoExists = await fs.access(authenticatedUserDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (authenticatedUserDtoExists) {
|
|
const authenticatedUserDtoContent = await fs.readFile(authenticatedUserDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (authenticatedUserSchema.required) {
|
|
for (const requiredProp of authenticatedUserSchema.required) {
|
|
expect(authenticatedUserDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (authenticatedUserSchema.properties) {
|
|
for (const propName of Object.keys(authenticatedUserSchema.properties)) {
|
|
expect(authenticatedUserDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test SignupParamsDTO
|
|
const signupParamsSchema = schemas['SignupParamsDTO'];
|
|
const signupParamsDtoPath = path.join(generatedTypesDir, 'SignupParamsDTO.ts');
|
|
const signupParamsDtoExists = await fs.access(signupParamsDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (signupParamsDtoExists) {
|
|
const signupParamsDtoContent = await fs.readFile(signupParamsDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (signupParamsSchema.required) {
|
|
for (const requiredProp of signupParamsSchema.required) {
|
|
expect(signupParamsDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (signupParamsSchema.properties) {
|
|
for (const propName of Object.keys(signupParamsSchema.properties)) {
|
|
expect(signupParamsDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test LoginParamsDTO
|
|
const loginParamsSchema = schemas['LoginParamsDTO'];
|
|
const loginParamsDtoPath = path.join(generatedTypesDir, 'LoginParamsDTO.ts');
|
|
const loginParamsDtoExists = await fs.access(loginParamsDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (loginParamsDtoExists) {
|
|
const loginParamsDtoContent = await fs.readFile(loginParamsDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (loginParamsSchema.required) {
|
|
for (const requiredProp of loginParamsSchema.required) {
|
|
expect(loginParamsDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (loginParamsSchema.properties) {
|
|
for (const propName of Object.keys(loginParamsSchema.properties)) {
|
|
expect(loginParamsDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test ForgotPasswordDTO
|
|
const forgotPasswordSchema = schemas['ForgotPasswordDTO'];
|
|
const forgotPasswordDtoPath = path.join(generatedTypesDir, 'ForgotPasswordDTO.ts');
|
|
const forgotPasswordDtoExists = await fs.access(forgotPasswordDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (forgotPasswordDtoExists) {
|
|
const forgotPasswordDtoContent = await fs.readFile(forgotPasswordDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (forgotPasswordSchema.required) {
|
|
for (const requiredProp of forgotPasswordSchema.required) {
|
|
expect(forgotPasswordDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (forgotPasswordSchema.properties) {
|
|
for (const propName of Object.keys(forgotPasswordSchema.properties)) {
|
|
expect(forgotPasswordDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test ResetPasswordDTO
|
|
const resetPasswordSchema = schemas['ResetPasswordDTO'];
|
|
const resetPasswordDtoPath = path.join(generatedTypesDir, 'ResetPasswordDTO.ts');
|
|
const resetPasswordDtoExists = await fs.access(resetPasswordDtoPath).then(() => true).catch(() => false);
|
|
|
|
if (resetPasswordDtoExists) {
|
|
const resetPasswordDtoContent = await fs.readFile(resetPasswordDtoPath, 'utf-8');
|
|
|
|
// Check that all required properties are present
|
|
if (resetPasswordSchema.required) {
|
|
for (const requiredProp of resetPasswordSchema.required) {
|
|
expect(resetPasswordDtoContent).toContain(requiredProp);
|
|
}
|
|
}
|
|
|
|
// Check that all properties are present
|
|
if (resetPasswordSchema.properties) {
|
|
for (const propName of Object.keys(resetPasswordSchema.properties)) {
|
|
expect(resetPasswordDtoContent).toContain(propName);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should have auth types defined in tbd folder', async () => {
|
|
// Check if auth types exist in tbd folder (similar to admin types)
|
|
const tbdDir = path.join(websiteTypesDir, 'tbd');
|
|
const tbdFiles = await fs.readdir(tbdDir).catch(() => []);
|
|
|
|
// Auth types might be in a separate file or combined with existing types
|
|
// For now, we'll check if the generated types are properly available
|
|
const generatedFiles = await fs.readdir(generatedTypesDir);
|
|
const authGenerated = generatedFiles.filter(f =>
|
|
f.includes('Auth') ||
|
|
f.includes('Login') ||
|
|
f.includes('Signup') ||
|
|
f.includes('Session') ||
|
|
f.includes('Forgot') ||
|
|
f.includes('Reset')
|
|
);
|
|
|
|
expect(authGenerated.length).toBeGreaterThanOrEqual(6);
|
|
});
|
|
|
|
it('should have auth types re-exported from main types file', async () => {
|
|
// Check if there's an auth.ts file or if types are exported elsewhere
|
|
const authTypesPath = path.join(websiteTypesDir, 'auth.ts');
|
|
const authTypesExists = await fs.access(authTypesPath).then(() => true).catch(() => false);
|
|
|
|
if (authTypesExists) {
|
|
const authTypesContent = await fs.readFile(authTypesPath, 'utf-8');
|
|
|
|
// Verify re-exports
|
|
expect(authTypesContent).toContain('AuthSessionDTO');
|
|
expect(authTypesContent).toContain('AuthenticatedUserDTO');
|
|
expect(authTypesContent).toContain('SignupParamsDTO');
|
|
expect(authTypesContent).toContain('LoginParamsDTO');
|
|
expect(authTypesContent).toContain('ForgotPasswordDTO');
|
|
expect(authTypesContent).toContain('ResetPasswordDTO');
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Auth API Client Contract', () => {
|
|
it('should have AuthApiClient defined', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientExists = await fs.access(authApiClientPath).then(() => true).catch(() => false);
|
|
|
|
expect(authApiClientExists).toBe(true);
|
|
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify class definition
|
|
expect(authApiClientContent).toContain('export class AuthApiClient');
|
|
expect(authApiClientContent).toContain('extends BaseApiClient');
|
|
|
|
// Verify methods exist
|
|
expect(authApiClientContent).toContain('signup');
|
|
expect(authApiClientContent).toContain('login');
|
|
expect(authApiClientContent).toContain('getSession');
|
|
expect(authApiClientContent).toContain('logout');
|
|
expect(authApiClientContent).toContain('forgotPassword');
|
|
expect(authApiClientContent).toContain('resetPassword');
|
|
|
|
// Verify method signatures
|
|
expect(authApiClientContent).toContain('signup(params: SignupParamsDTO)');
|
|
expect(authApiClientContent).toContain('login(params: LoginParamsDTO)');
|
|
expect(authApiClientContent).toContain('getSession()');
|
|
expect(authApiClientContent).toContain('logout()');
|
|
expect(authApiClientContent).toContain('forgotPassword(params: ForgotPasswordDTO)');
|
|
expect(authApiClientContent).toContain('resetPassword(params: ResetPasswordDTO)');
|
|
});
|
|
|
|
it('should have proper request construction in signup method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify POST request with params
|
|
expect(authApiClientContent).toContain("return this.post<AuthSessionDTO>('/auth/signup', params)");
|
|
});
|
|
|
|
it('should have proper request construction in login method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify POST request with params
|
|
expect(authApiClientContent).toContain("return this.post<AuthSessionDTO>('/auth/login', params)");
|
|
});
|
|
|
|
it('should have proper request construction in getSession method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify GET request with allowUnauthenticated option
|
|
expect(authApiClientContent).toContain("return this.request<AuthSessionDTO | null>('GET', '/auth/session', undefined, {");
|
|
expect(authApiClientContent).toContain('allowUnauthenticated: true');
|
|
});
|
|
|
|
it('should have proper request construction in logout method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify POST request with empty body
|
|
expect(authApiClientContent).toContain("return this.post<void>('/auth/logout', {})");
|
|
});
|
|
|
|
it('should have proper request construction in forgotPassword method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify POST request with params
|
|
expect(authApiClientContent).toContain("return this.post<{ message: string; magicLink?: string }>('/auth/forgot-password', params)");
|
|
});
|
|
|
|
it('should have proper request construction in resetPassword method', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify POST request with params
|
|
expect(authApiClientContent).toContain("return this.post<{ message: string }>('/auth/reset-password', params)");
|
|
});
|
|
});
|
|
|
|
describe('Request Correctness Tests', () => {
|
|
it('should validate SignupParamsDTO required fields', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['SignupParamsDTO'];
|
|
|
|
// Verify all required fields are present
|
|
expect(schema.required).toContain('email');
|
|
expect(schema.required).toContain('password');
|
|
expect(schema.required).toContain('displayName');
|
|
|
|
// Verify no extra required fields
|
|
expect(schema.required.length).toBe(3);
|
|
});
|
|
|
|
it('should validate SignupParamsDTO optional fields', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['SignupParamsDTO'];
|
|
|
|
// Verify optional fields are not required
|
|
expect(schema.required).not.toContain('username');
|
|
expect(schema.required).not.toContain('iracingCustomerId');
|
|
expect(schema.required).not.toContain('primaryDriverId');
|
|
expect(schema.required).not.toContain('avatarUrl');
|
|
|
|
// Verify optional fields exist
|
|
expect(schema.properties?.username).toBeDefined();
|
|
expect(schema.properties?.iracingCustomerId).toBeDefined();
|
|
expect(schema.properties?.primaryDriverId).toBeDefined();
|
|
expect(schema.properties?.avatarUrl).toBeDefined();
|
|
});
|
|
|
|
it('should validate LoginParamsDTO required fields', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['LoginParamsDTO'];
|
|
|
|
// Verify all required fields are present
|
|
expect(schema.required).toContain('email');
|
|
expect(schema.required).toContain('password');
|
|
|
|
// Verify no extra required fields
|
|
expect(schema.required.length).toBe(2);
|
|
});
|
|
|
|
it('should validate LoginParamsDTO optional fields', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['LoginParamsDTO'];
|
|
|
|
// Verify optional fields are not required
|
|
expect(schema.required).not.toContain('rememberMe');
|
|
|
|
// Verify optional fields exist
|
|
expect(schema.properties?.rememberMe).toBeDefined();
|
|
expect(schema.properties?.rememberMe?.type).toBe('boolean');
|
|
expect(schema.properties?.rememberMe?.default).toBe(false);
|
|
});
|
|
|
|
it('should validate ForgotPasswordDTO structure', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['ForgotPasswordDTO'];
|
|
|
|
// Verify all required fields
|
|
expect(schema.required).toContain('email');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
});
|
|
|
|
it('should validate ResetPasswordDTO structure', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['ResetPasswordDTO'];
|
|
|
|
// Verify all required fields
|
|
expect(schema.required).toContain('token');
|
|
expect(schema.required).toContain('newPassword');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.token?.type).toBe('string');
|
|
expect(schema.properties?.newPassword?.type).toBe('string');
|
|
});
|
|
|
|
it('should validate AuthSessionDTO structure', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Verify all required fields
|
|
expect(schema.required).toContain('token');
|
|
expect(schema.required).toContain('user');
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.token?.type).toBe('string');
|
|
expect(schema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
});
|
|
|
|
it('should validate AuthenticatedUserDTO structure', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const schema = spec.components.schemas['AuthenticatedUserDTO'];
|
|
|
|
// Verify all required fields
|
|
const requiredFields = ['userId', 'email', 'displayName'];
|
|
for (const field of requiredFields) {
|
|
expect(schema.required).toContain(field);
|
|
}
|
|
|
|
// Verify field types
|
|
expect(schema.properties?.userId?.type).toBe('string');
|
|
expect(schema.properties?.email?.type).toBe('string');
|
|
expect(schema.properties?.displayName?.type).toBe('string');
|
|
expect(schema.properties?.role?.type).toBe('string');
|
|
|
|
// Verify optional fields are nullable
|
|
expect(schema.properties?.avatarUrl?.nullable).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Response Handling Tests', () => {
|
|
it('should handle successful signup response', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const authSessionSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Verify response structure
|
|
expect(authSessionSchema.properties?.token).toBeDefined();
|
|
expect(authSessionSchema.properties?.user).toBeDefined();
|
|
expect(authSessionSchema.properties?.token?.type).toBe('string');
|
|
expect(authSessionSchema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
expect(authSessionSchema.required).toContain('token');
|
|
expect(authSessionSchema.required).toContain('user');
|
|
});
|
|
|
|
it('should handle successful login response', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const authSessionSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Verify response structure
|
|
expect(authSessionSchema.properties?.token).toBeDefined();
|
|
expect(authSessionSchema.properties?.user).toBeDefined();
|
|
expect(authSessionSchema.properties?.token?.type).toBe('string');
|
|
expect(authSessionSchema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
expect(authSessionSchema.required).toContain('token');
|
|
expect(authSessionSchema.required).toContain('user');
|
|
});
|
|
|
|
it('should handle session response with all required fields', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const authSessionSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Verify all required fields are present
|
|
for (const field of ['token', 'user']) {
|
|
expect(authSessionSchema.required).toContain(field);
|
|
expect(authSessionSchema.properties?.[field]).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should handle optional fields in user response correctly', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const userSchema = spec.components.schemas['AuthenticatedUserDTO'];
|
|
|
|
// Verify optional fields are nullable
|
|
expect(userSchema.properties?.avatarUrl?.nullable).toBe(true);
|
|
|
|
// Verify optional fields are not in required array
|
|
expect(userSchema.required).not.toContain('avatarUrl');
|
|
expect(userSchema.required).not.toContain('primaryDriverId');
|
|
expect(userSchema.required).not.toContain('companyId');
|
|
expect(userSchema.required).not.toContain('role');
|
|
});
|
|
|
|
it('should handle forgot-password response correctly', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const forgotPasswordPath = spec.paths['/auth/forgot-password']?.post;
|
|
expect(forgotPasswordPath).toBeDefined();
|
|
|
|
// Verify response structure
|
|
const response200 = forgotPasswordPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
});
|
|
|
|
it('should handle reset-password response correctly', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const resetPasswordPath = spec.paths['/auth/reset-password']?.post;
|
|
expect(resetPasswordPath).toBeDefined();
|
|
|
|
// Verify response structure
|
|
const response200 = resetPasswordPath.responses['200'];
|
|
expect(response200).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Error Handling Tests', () => {
|
|
it('should document 400 Bad Request response for invalid signup input', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const signupPath = spec.paths['/auth/signup']?.post;
|
|
|
|
// Check if 400 response is documented
|
|
if (signupPath.responses['400']) {
|
|
expect(signupPath.responses['400']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 400 Bad Request response for invalid login input', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const loginPath = spec.paths['/auth/login']?.post;
|
|
|
|
// Check if 400 response is documented
|
|
if (loginPath.responses['400']) {
|
|
expect(loginPath.responses['400']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 401 Unauthorized response for login endpoint', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const loginPath = spec.paths['/auth/login']?.post;
|
|
|
|
// Check if 401 response is documented
|
|
if (loginPath.responses['401']) {
|
|
expect(loginPath.responses['401']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 400 Bad Request response for invalid forgot-password input', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const forgotPasswordPath = spec.paths['/auth/forgot-password']?.post;
|
|
|
|
// Check if 400 response is documented
|
|
if (forgotPasswordPath.responses['400']) {
|
|
expect(forgotPasswordPath.responses['400']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 400 Bad Request response for invalid reset-password input', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const resetPasswordPath = spec.paths['/auth/reset-password']?.post;
|
|
|
|
// Check if 400 response is documented
|
|
if (resetPasswordPath.responses['400']) {
|
|
expect(resetPasswordPath.responses['400']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 401 Unauthorized response for protected endpoints', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Session endpoint should require authentication
|
|
const sessionPath = spec.paths['/auth/session']?.get;
|
|
|
|
// Check if 401 responses are documented
|
|
if (sessionPath.responses['401']) {
|
|
expect(sessionPath.responses['401']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should document 500 Internal Server Error response', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const signupPath = spec.paths['/auth/signup']?.post;
|
|
const loginPath = spec.paths['/auth/login']?.post;
|
|
|
|
// Check if 500 response is documented for auth endpoints
|
|
if (signupPath.responses['500']) {
|
|
expect(signupPath.responses['500']).toBeDefined();
|
|
}
|
|
if (loginPath.responses['500']) {
|
|
expect(loginPath.responses['500']).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should have proper error handling in AuthApiClient', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify BaseApiClient is extended (which provides error handling)
|
|
expect(authApiClientContent).toContain('extends BaseApiClient');
|
|
|
|
// Verify methods use BaseApiClient methods (which handle errors)
|
|
expect(authApiClientContent).toContain('this.post<');
|
|
expect(authApiClientContent).toContain('this.get<');
|
|
expect(authApiClientContent).toContain('this.request<');
|
|
});
|
|
});
|
|
|
|
describe('Semantic Guarantee Tests', () => {
|
|
it('should maintain consistency between request and response schemas', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Verify signup request/response consistency
|
|
const signupInputSchema = spec.components.schemas['SignupParamsDTO'];
|
|
const signupOutputSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Output should contain token and user
|
|
expect(signupOutputSchema.properties?.token).toBeDefined();
|
|
expect(signupOutputSchema.properties?.user).toBeDefined();
|
|
|
|
// Verify login request/response consistency
|
|
const loginInputSchema = spec.components.schemas['LoginParamsDTO'];
|
|
const loginOutputSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Output should contain token and user
|
|
expect(loginOutputSchema.properties?.token).toBeDefined();
|
|
expect(loginOutputSchema.properties?.user).toBeDefined();
|
|
});
|
|
|
|
it('should validate semantic consistency in auth session', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const authSessionSchema = spec.components.schemas['AuthSessionDTO'];
|
|
const userSchema = spec.components.schemas['AuthenticatedUserDTO'];
|
|
|
|
// Verify session has token and user
|
|
expect(authSessionSchema.properties?.token?.type).toBe('string');
|
|
expect(authSessionSchema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
|
|
// Verify user has required fields
|
|
expect(userSchema.required).toContain('userId');
|
|
expect(userSchema.required).toContain('email');
|
|
expect(userSchema.required).toContain('displayName');
|
|
});
|
|
|
|
it('should validate idempotency for logout', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Check if logout endpoint exists
|
|
const logoutPath = spec.paths['/auth/logout']?.post;
|
|
if (logoutPath) {
|
|
// Verify it accepts an empty body (idempotent operation)
|
|
const requestBody = logoutPath.requestBody;
|
|
if (requestBody && requestBody.content && requestBody.content['application/json']) {
|
|
const schema = requestBody.content['application/json'].schema;
|
|
// Empty body or minimal body
|
|
expect(schema).toBeDefined();
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should validate uniqueness constraints for user email', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const userSchema = spec.components.schemas['AuthenticatedUserDTO'];
|
|
|
|
// Verify email is a required field
|
|
expect(userSchema.required).toContain('email');
|
|
expect(userSchema.properties?.email?.type).toBe('string');
|
|
|
|
// Check for signup endpoint
|
|
const signupPath = spec.paths['/auth/signup']?.post;
|
|
if (signupPath) {
|
|
// Verify email is required in request body
|
|
const requestBody = signupPath.requestBody;
|
|
if (requestBody && requestBody.content && requestBody.content['application/json']) {
|
|
const schema = requestBody.content['application/json'].schema;
|
|
if (schema && schema.$ref) {
|
|
// This would reference SignupParamsDTO which should have email as required
|
|
expect(schema.$ref).toContain('SignupParamsDTO');
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should validate consistency between request and response types', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Verify all DTOs have consistent type definitions
|
|
const dtos = [
|
|
'AuthSessionDTO',
|
|
'AuthenticatedUserDTO',
|
|
'SignupParamsDTO',
|
|
'LoginParamsDTO',
|
|
'ForgotPasswordDTO',
|
|
'ResetPasswordDTO',
|
|
];
|
|
|
|
for (const dtoName of dtos) {
|
|
const schema = spec.components.schemas[dtoName];
|
|
expect(schema).toBeDefined();
|
|
expect(schema.type).toBe('object');
|
|
|
|
// All should have properties defined
|
|
expect(schema.properties).toBeDefined();
|
|
|
|
// All should have required fields (even if empty array)
|
|
expect(schema.required).toBeDefined();
|
|
}
|
|
});
|
|
|
|
it('should validate semantic consistency in auth session token', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const authSessionSchema = spec.components.schemas['AuthSessionDTO'];
|
|
|
|
// Verify token is a string (JWT format)
|
|
expect(authSessionSchema.properties?.token?.type).toBe('string');
|
|
|
|
// Verify user is a reference to AuthenticatedUserDTO
|
|
expect(authSessionSchema.properties?.user?.$ref).toBe('#/components/schemas/AuthenticatedUserDTO');
|
|
});
|
|
|
|
it('should validate pagination is not applicable for auth endpoints', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
// Auth endpoints should not have pagination
|
|
const authEndpoints = [
|
|
'/auth/signup',
|
|
'/auth/login',
|
|
'/auth/session',
|
|
'/auth/logout',
|
|
'/auth/forgot-password',
|
|
'/auth/reset-password',
|
|
];
|
|
|
|
for (const endpoint of authEndpoints) {
|
|
const path = spec.paths[endpoint];
|
|
if (path) {
|
|
// Check if there are any query parameters for pagination
|
|
const methods = Object.keys(path);
|
|
for (const method of methods) {
|
|
const operation = path[method];
|
|
if (operation.parameters) {
|
|
const paramNames = operation.parameters.map((p: any) => p.name);
|
|
// Auth endpoints should not have page/limit parameters
|
|
expect(paramNames).not.toContain('page');
|
|
expect(paramNames).not.toContain('limit');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Auth Module Integration Tests', () => {
|
|
it('should have consistent types between API DTOs and website types', async () => {
|
|
const content = await fs.readFile(openapiPath, 'utf-8');
|
|
const spec: OpenAPISpec = JSON.parse(content);
|
|
|
|
const generatedFiles = await fs.readdir(generatedTypesDir);
|
|
const generatedDTOs = generatedFiles
|
|
.filter(f => f.endsWith('.ts'))
|
|
.map(f => f.replace('.ts', ''));
|
|
|
|
// Check all auth DTOs exist in generated types
|
|
const authDTOs = [
|
|
'AuthSessionDTO',
|
|
'AuthenticatedUserDTO',
|
|
'SignupParamsDTO',
|
|
'LoginParamsDTO',
|
|
'ForgotPasswordDTO',
|
|
'ResetPasswordDTO',
|
|
];
|
|
|
|
for (const dtoName of authDTOs) {
|
|
expect(spec.components.schemas[dtoName]).toBeDefined();
|
|
expect(generatedDTOs).toContain(dtoName);
|
|
}
|
|
});
|
|
|
|
it('should have AuthApiClient methods matching API endpoints', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify signup method exists and uses correct endpoint
|
|
expect(authApiClientContent).toContain('async signup');
|
|
expect(authApiClientContent).toContain("return this.post<AuthSessionDTO>('/auth/signup', params)");
|
|
|
|
// Verify login method exists and uses correct endpoint
|
|
expect(authApiClientContent).toContain('async login');
|
|
expect(authApiClientContent).toContain("return this.post<AuthSessionDTO>('/auth/login', params)");
|
|
|
|
// Verify getSession method exists and uses correct endpoint
|
|
expect(authApiClientContent).toContain('async getSession');
|
|
expect(authApiClientContent).toContain("return this.request<AuthSessionDTO | null>('GET', '/auth/session', undefined, {");
|
|
|
|
// Verify logout method exists and uses correct endpoint
|
|
expect(authApiClientContent).toContain('async logout');
|
|
expect(authApiClientContent).toContain("return this.post<void>('/auth/logout', {})");
|
|
});
|
|
|
|
it('should have proper error handling in AuthApiClient', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify BaseApiClient is extended (which provides error handling)
|
|
expect(authApiClientContent).toContain('extends BaseApiClient');
|
|
|
|
// Verify methods use BaseApiClient methods (which handle errors)
|
|
expect(authApiClientContent).toContain('this.post<');
|
|
expect(authApiClientContent).toContain('this.get<');
|
|
expect(authApiClientContent).toContain('this.request<');
|
|
});
|
|
|
|
it('should have consistent type imports in AuthApiClient', async () => {
|
|
const authApiClientPath = path.join(apiRoot, 'apps/website/lib/api/auth/AuthApiClient.ts');
|
|
const authApiClientContent = await fs.readFile(authApiClientPath, 'utf-8');
|
|
|
|
// Verify all required types are imported
|
|
expect(authApiClientContent).toContain('AuthSessionDTO');
|
|
expect(authApiClientContent).toContain('LoginParamsDTO');
|
|
expect(authApiClientContent).toContain('SignupParamsDTO');
|
|
expect(authApiClientContent).toContain('ForgotPasswordDTO');
|
|
expect(authApiClientContent).toContain('ResetPasswordDTO');
|
|
});
|
|
});
|
|
});
|