Couverture des processors (RefreshToken, RequestPasswordReset, ResetPassword, SwitchRole, UpdateUserRoles), des query handlers (HasGradesInPeriod, HasStudentsInClass), des messaging handlers (SendActivationConfirmation, SendPasswordResetEmail), et côté frontend des modules auth, roles, monitoring, types et E2E tokens.
169 lines
4.6 KiB
TypeScript
169 lines
4.6 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
|
|
/**
|
|
* Unit tests for the API config module (config.ts).
|
|
*
|
|
* Tests getApiBaseUrl() and getCurrentTenant() which depend on:
|
|
* - $app/environment (browser flag)
|
|
* - $env/dynamic/public (environment variables)
|
|
* - window.location (hostname, protocol)
|
|
*/
|
|
|
|
// Mutable env object so we can change values per-test
|
|
const mockEnv: Record<string, string | undefined> = {};
|
|
|
|
// Mock $app/environment - default to browser=true
|
|
let mockBrowser = true;
|
|
vi.mock('$app/environment', () => ({
|
|
get browser() {
|
|
return mockBrowser;
|
|
}
|
|
}));
|
|
|
|
// Mock $env/dynamic/public
|
|
vi.mock('$env/dynamic/public', () => ({
|
|
env: new Proxy(mockEnv, {
|
|
get(target, prop: string) {
|
|
return target[prop];
|
|
}
|
|
})
|
|
}));
|
|
|
|
describe('API config', () => {
|
|
let configModule: typeof import('$lib/api/config');
|
|
|
|
beforeEach(async () => {
|
|
// Reset env between tests
|
|
Object.keys(mockEnv).forEach((key) => delete mockEnv[key]);
|
|
mockBrowser = true;
|
|
|
|
// Reset modules to get a fresh import
|
|
vi.resetModules();
|
|
configModule = await import('$lib/api/config');
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
// ==========================================================================
|
|
// getApiBaseUrl
|
|
// ==========================================================================
|
|
describe('getApiBaseUrl', () => {
|
|
it('should return protocol://hostname:port/api when in browser with PUBLIC_API_PORT', () => {
|
|
mockBrowser = true;
|
|
mockEnv['PUBLIC_API_PORT'] = '18000';
|
|
|
|
// jsdom provides window.location - set it for the test
|
|
// Default jsdom location is http://localhost, so we work with that
|
|
const result = configModule.getApiBaseUrl();
|
|
|
|
expect(result).toBe(`${window.location.protocol}//${window.location.hostname}:18000/api`);
|
|
});
|
|
|
|
it('should return /api when in browser without PUBLIC_API_PORT', () => {
|
|
mockBrowser = true;
|
|
// PUBLIC_API_PORT is not set (undefined)
|
|
|
|
const result = configModule.getApiBaseUrl();
|
|
|
|
expect(result).toBe('/api');
|
|
});
|
|
|
|
it('should return PUBLIC_API_URL when in SSR and PUBLIC_API_URL is set', () => {
|
|
mockBrowser = false;
|
|
mockEnv['PUBLIC_API_URL'] = 'https://api.classeo.fr/api';
|
|
|
|
const result = configModule.getApiBaseUrl();
|
|
|
|
expect(result).toBe('https://api.classeo.fr/api');
|
|
});
|
|
|
|
it('should return http://php:8000/api as SSR fallback', () => {
|
|
mockBrowser = false;
|
|
// PUBLIC_API_URL is not set
|
|
|
|
const result = configModule.getApiBaseUrl();
|
|
|
|
expect(result).toBe('http://php:8000/api');
|
|
});
|
|
});
|
|
|
|
// ==========================================================================
|
|
// getCurrentTenant
|
|
// ==========================================================================
|
|
describe('getCurrentTenant', () => {
|
|
it('should return null when not in browser', () => {
|
|
mockBrowser = false;
|
|
|
|
const result = configModule.getCurrentTenant();
|
|
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should extract subdomain correctly from hostname', () => {
|
|
mockBrowser = true;
|
|
mockEnv['PUBLIC_BASE_DOMAIN'] = 'classeo.local';
|
|
|
|
// Override window.location.hostname for this test
|
|
// jsdom default is "localhost" which won't match "classeo.local"
|
|
// We need to use Object.defineProperty since location is readonly
|
|
const originalHostname = window.location.hostname;
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: 'ecole-alpha.classeo.local' },
|
|
writable: true
|
|
});
|
|
|
|
const result = configModule.getCurrentTenant();
|
|
|
|
expect(result).toBe('ecole-alpha');
|
|
|
|
// Restore
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: originalHostname },
|
|
writable: true
|
|
});
|
|
});
|
|
|
|
it('should return null for www subdomain', () => {
|
|
mockBrowser = true;
|
|
mockEnv['PUBLIC_BASE_DOMAIN'] = 'classeo.fr';
|
|
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: 'www.classeo.fr' },
|
|
writable: true
|
|
});
|
|
|
|
const result = configModule.getCurrentTenant();
|
|
|
|
expect(result).toBeNull();
|
|
|
|
// Restore
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: 'localhost' },
|
|
writable: true
|
|
});
|
|
});
|
|
|
|
it('should return null when hostname does not match base domain', () => {
|
|
mockBrowser = true;
|
|
mockEnv['PUBLIC_BASE_DOMAIN'] = 'classeo.fr';
|
|
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: 'other-domain.com' },
|
|
writable: true
|
|
});
|
|
|
|
const result = configModule.getCurrentTenant();
|
|
|
|
expect(result).toBeNull();
|
|
|
|
// Restore
|
|
Object.defineProperty(window, 'location', {
|
|
value: { ...window.location, hostname: 'localhost' },
|
|
writable: true
|
|
});
|
|
});
|
|
});
|
|
});
|