fix issues
This commit is contained in:
@@ -205,16 +205,88 @@ test.describe('Website Pages - TypeORM Integration', () => {
|
||||
!msg.includes('connection refused') &&
|
||||
!msg.includes('failed to load resource') &&
|
||||
!msg.includes('network error') &&
|
||||
!msg.includes('cors') &&
|
||||
!msg.includes('api');
|
||||
!msg.includes('cors');
|
||||
});
|
||||
|
||||
// Check for critical runtime errors that should never occur
|
||||
const criticalErrors = errors.filter(error => {
|
||||
const msg = error.message.toLowerCase();
|
||||
return msg.includes('no queryclient set') ||
|
||||
msg.includes('use queryclientprovider') ||
|
||||
msg.includes('console.groupcollapsed is not a function') ||
|
||||
msg.includes('console.groupend is not a function');
|
||||
});
|
||||
|
||||
if (unexpectedErrors.length > 0) {
|
||||
console.log(`[TEST DEBUG] Unexpected errors on ${path}:`, unexpectedErrors);
|
||||
}
|
||||
|
||||
// Allow some errors in test environment due to network/API issues
|
||||
expect(unexpectedErrors.length).toBeLessThanOrEqual(0);
|
||||
if (criticalErrors.length > 0) {
|
||||
console.log(`[TEST DEBUG] CRITICAL errors on ${path}:`, criticalErrors);
|
||||
throw new Error(`Critical runtime errors on ${path}: ${JSON.stringify(criticalErrors)}`);
|
||||
}
|
||||
|
||||
// Fail on any unexpected errors including DI binding failures
|
||||
expect(unexpectedErrors.length).toBe(0);
|
||||
}
|
||||
});
|
||||
|
||||
test('detect DI binding failures and missing metadata on boot', async ({ page }) => {
|
||||
// Test critical routes that would trigger DI container creation
|
||||
const criticalRoutes = [
|
||||
'/leagues',
|
||||
'/dashboard',
|
||||
'/teams',
|
||||
'/drivers',
|
||||
'/races',
|
||||
'/leaderboards'
|
||||
];
|
||||
|
||||
for (const path of criticalRoutes) {
|
||||
const capture = new ConsoleErrorCapture(page);
|
||||
|
||||
const response = await page.goto(`${WEBSITE_BASE_URL}${path}`);
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
// Check for 500 errors
|
||||
const status = response?.status();
|
||||
if (status === 500) {
|
||||
console.log(`[TEST DEBUG] 500 error on ${path}`);
|
||||
const bodyText = await page.textContent('body');
|
||||
console.log(`[TEST DEBUG] Body content: ${bodyText?.substring(0, 500)}`);
|
||||
}
|
||||
|
||||
// Check for DI-related errors in console
|
||||
const errors = capture.getErrors();
|
||||
const diErrors = errors.filter(error => {
|
||||
const msg = error.message.toLowerCase();
|
||||
return msg.includes('binding') ||
|
||||
msg.includes('metadata') ||
|
||||
msg.includes('inversify') ||
|
||||
msg.includes('symbol') ||
|
||||
msg.includes('no binding') ||
|
||||
msg.includes('not bound');
|
||||
});
|
||||
|
||||
// Check for React Query provider errors
|
||||
const queryClientErrors = errors.filter(error => {
|
||||
const msg = error.message.toLowerCase();
|
||||
return msg.includes('no queryclient set') ||
|
||||
msg.includes('use queryclientprovider');
|
||||
});
|
||||
|
||||
if (diErrors.length > 0) {
|
||||
console.log(`[TEST DEBUG] DI errors on ${path}:`, diErrors);
|
||||
}
|
||||
|
||||
if (queryClientErrors.length > 0) {
|
||||
console.log(`[TEST DEBUG] QueryClient errors on ${path}:`, queryClientErrors);
|
||||
throw new Error(`QueryClient provider missing on ${path}: ${JSON.stringify(queryClientErrors)}`);
|
||||
}
|
||||
|
||||
// Fail on DI errors or 500 status
|
||||
expect(diErrors.length).toBe(0);
|
||||
expect(status).not.toBe(500);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
174
tests/integration/website-di-container.test.ts
Normal file
174
tests/integration/website-di-container.test.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
|
||||
import { createContainer } from '../../apps/website/lib/di/container';
|
||||
import {
|
||||
SESSION_SERVICE_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_SERVICE_TOKEN,
|
||||
LEAGUE_SERVICE_TOKEN,
|
||||
AUTH_SERVICE_TOKEN,
|
||||
DRIVER_SERVICE_TOKEN,
|
||||
TEAM_SERVICE_TOKEN,
|
||||
RACE_SERVICE_TOKEN,
|
||||
DASHBOARD_SERVICE_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
CONFIG_TOKEN,
|
||||
LEAGUE_API_CLIENT_TOKEN,
|
||||
AUTH_API_CLIENT_TOKEN,
|
||||
DRIVER_API_CLIENT_TOKEN,
|
||||
TEAM_API_CLIENT_TOKEN,
|
||||
RACE_API_CLIENT_TOKEN,
|
||||
} from '../../apps/website/lib/di/tokens';
|
||||
|
||||
/**
|
||||
* Integration test for website DI container
|
||||
*
|
||||
* This test verifies that all critical DI bindings are properly configured
|
||||
* and that the container can resolve all required services without throwing
|
||||
* binding errors or missing metadata errors.
|
||||
*
|
||||
* This is a fast, non-Playwright test that runs in CI to catch DI issues early.
|
||||
*/
|
||||
describe('Website DI Container Integration', () => {
|
||||
let container: ReturnType<typeof createContainer>;
|
||||
let originalEnv: NodeJS.ProcessEnv;
|
||||
|
||||
beforeAll(() => {
|
||||
// Save original environment
|
||||
originalEnv = { ...process.env };
|
||||
|
||||
// Set up minimal environment for DI container to work
|
||||
// The container needs API_BASE_URL to initialize
|
||||
process.env.API_BASE_URL = 'http://localhost:3001';
|
||||
process.env.NEXT_PUBLIC_API_BASE_URL = 'http://localhost:3001';
|
||||
process.env.NODE_ENV = 'test';
|
||||
|
||||
// Create the container once for all tests
|
||||
container = createContainer();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// Restore original environment
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
it('creates container successfully', () => {
|
||||
expect(container).toBeDefined();
|
||||
expect(container).not.toBeNull();
|
||||
});
|
||||
|
||||
it('resolves core services without errors', () => {
|
||||
// Core services that should always be available
|
||||
expect(() => container.get(LOGGER_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(CONFIG_TOKEN)).not.toThrow();
|
||||
|
||||
const logger = container.get(LOGGER_TOKEN);
|
||||
const config = container.get(CONFIG_TOKEN);
|
||||
|
||||
expect(logger).toBeDefined();
|
||||
expect(config).toBeDefined();
|
||||
});
|
||||
|
||||
it('resolves API clients without errors', () => {
|
||||
// API clients that services depend on
|
||||
const apiClients = [
|
||||
LEAGUE_API_CLIENT_TOKEN,
|
||||
AUTH_API_CLIENT_TOKEN,
|
||||
DRIVER_API_CLIENT_TOKEN,
|
||||
TEAM_API_CLIENT_TOKEN,
|
||||
RACE_API_CLIENT_TOKEN,
|
||||
];
|
||||
|
||||
for (const token of apiClients) {
|
||||
expect(() => container.get(token)).not.toThrow();
|
||||
const client = container.get(token);
|
||||
expect(client).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
it('resolves auth services including SessionService (critical for Symbol(Service.Session))', () => {
|
||||
// This specifically tests for the Symbol(Service.Session) binding issue
|
||||
expect(() => container.get(SESSION_SERVICE_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(AUTH_SERVICE_TOKEN)).not.toThrow();
|
||||
|
||||
const sessionService = container.get(SESSION_SERVICE_TOKEN);
|
||||
const authService = container.get(AUTH_SERVICE_TOKEN);
|
||||
|
||||
expect(sessionService).toBeDefined();
|
||||
expect(authService).toBeDefined();
|
||||
|
||||
// Verify the services have expected methods
|
||||
expect(typeof sessionService.getSession).toBe('function');
|
||||
expect(typeof authService.login).toBe('function');
|
||||
});
|
||||
|
||||
it('resolves league services including LeagueMembershipService (critical for metadata)', () => {
|
||||
// This specifically tests for the LeagueMembershipService metadata issue
|
||||
expect(() => container.get(LEAGUE_SERVICE_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(LEAGUE_MEMBERSHIP_SERVICE_TOKEN)).not.toThrow();
|
||||
|
||||
const leagueService = container.get(LEAGUE_SERVICE_TOKEN);
|
||||
const membershipService = container.get(LEAGUE_MEMBERSHIP_SERVICE_TOKEN);
|
||||
|
||||
expect(leagueService).toBeDefined();
|
||||
expect(membershipService).toBeDefined();
|
||||
|
||||
// Verify the services have expected methods
|
||||
expect(typeof leagueService.getAllLeagues).toBe('function');
|
||||
expect(typeof membershipService.getLeagueMemberships).toBe('function');
|
||||
});
|
||||
|
||||
it('resolves domain services without errors', () => {
|
||||
// Test other critical domain services
|
||||
expect(() => container.get(DRIVER_SERVICE_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(TEAM_SERVICE_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(RACE_SERVICE_TOKEN)).not.toThrow();
|
||||
expect(() => container.get(DASHBOARD_SERVICE_TOKEN)).not.toThrow();
|
||||
|
||||
const driverService = container.get(DRIVER_SERVICE_TOKEN);
|
||||
const teamService = container.get(TEAM_SERVICE_TOKEN);
|
||||
const raceService = container.get(RACE_SERVICE_TOKEN);
|
||||
const dashboardService = container.get(DASHBOARD_SERVICE_TOKEN);
|
||||
|
||||
expect(driverService).toBeDefined();
|
||||
expect(teamService).toBeDefined();
|
||||
expect(raceService).toBeDefined();
|
||||
expect(dashboardService).toBeDefined();
|
||||
});
|
||||
|
||||
it('resolves all services in a single operation (full container boot simulation)', () => {
|
||||
// This simulates what happens when the website boots and needs multiple services
|
||||
const tokens = [
|
||||
SESSION_SERVICE_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_SERVICE_TOKEN,
|
||||
LEAGUE_SERVICE_TOKEN,
|
||||
AUTH_SERVICE_TOKEN,
|
||||
DRIVER_SERVICE_TOKEN,
|
||||
TEAM_SERVICE_TOKEN,
|
||||
RACE_SERVICE_TOKEN,
|
||||
DASHBOARD_SERVICE_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
CONFIG_TOKEN,
|
||||
];
|
||||
|
||||
// Resolve all tokens - if any binding is missing or metadata is wrong, this will throw
|
||||
const services = tokens.map(token => {
|
||||
try {
|
||||
return container.get(token);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to resolve token ${token.toString()}: ${error.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
// Verify all services were resolved
|
||||
expect(services.length).toBe(tokens.length);
|
||||
services.forEach((service, index) => {
|
||||
expect(service).toBeDefined();
|
||||
expect(service).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws clear error for non-existent bindings', () => {
|
||||
const fakeToken = Symbol.for('Non.Existent.Service');
|
||||
|
||||
expect(() => container.get(fakeToken)).toThrow();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user