fix issues

This commit is contained in:
2026-01-02 00:21:24 +01:00
parent 79913bb45e
commit 8693dde21e
46 changed files with 1680 additions and 302 deletions

View File

@@ -34,7 +34,7 @@ describe('AuthController', () => {
const params: SignupParamsDTO = {
email: 'test@example.com',
password: 'password123',
displayName: 'Test User',
displayName: 'John Smith',
iracingCustomerId: '12345',
primaryDriverId: 'driver1',
avatarUrl: 'http://example.com/avatar.jpg',
@@ -44,7 +44,7 @@ describe('AuthController', () => {
user: {
userId: 'user1',
email: 'test@example.com',
displayName: 'Test User',
displayName: 'John Smith',
},
};
(service.signupWithEmail as Mock).mockResolvedValue(session);
@@ -67,7 +67,7 @@ describe('AuthController', () => {
user: {
userId: 'user1',
email: 'test@example.com',
displayName: 'Test User',
displayName: 'John Smith',
},
};
(service.loginWithEmail as Mock).mockResolvedValue(session);
@@ -86,7 +86,7 @@ describe('AuthController', () => {
user: {
userId: 'user1',
email: 'test@example.com',
displayName: 'Test User',
displayName: 'John Smith',
},
};
(service.getCurrentSession as Mock).mockResolvedValue(session);

View File

@@ -69,7 +69,7 @@ describe('AuthService - New Methods', () => {
{ execute: vi.fn() } as any,
{ execute: vi.fn() } as any,
{ execute: vi.fn() } as any,
{ execute: vi.fn() } as any,
forgotPasswordUseCase as any,
{ execute: vi.fn() } as any,
{ execute: vi.fn() } as any,
new FakeAuthSessionPresenter() as any,
@@ -175,8 +175,9 @@ describe('AuthService - New Methods', () => {
const demoLoginPresenter = new FakeDemoLoginPresenter();
const mockUser = {
getId: () => ({ value: 'demo-user-123' }),
getDisplayName: () => 'Demo Driver',
getDisplayName: () => 'Alex Johnson',
getEmail: () => 'demo.driver@example.com',
getPrimaryDriverId: () => undefined,
};
const demoLoginUseCase = {
@@ -210,17 +211,20 @@ describe('AuthService - New Methods', () => {
const result = await service.demoLogin({ role: 'driver' });
expect(demoLoginUseCase.execute).toHaveBeenCalledWith({ role: 'driver' });
expect(identitySessionPort.createSession).toHaveBeenCalledWith({
id: 'demo-user-123',
displayName: 'Demo Driver',
email: 'demo.driver@example.com',
});
expect(identitySessionPort.createSession).toHaveBeenCalledWith(
{
id: 'demo-user-123',
displayName: 'Alex Johnson',
email: 'demo.driver@example.com',
},
undefined
);
expect(result).toEqual({
token: 'demo-token-123',
user: {
userId: 'demo-user-123',
email: 'demo.driver@example.com',
displayName: 'Demo Driver',
displayName: 'Alex Johnson',
},
});
});

View File

@@ -57,7 +57,7 @@ describe('AuthService', () => {
{
getCurrentSession: vi.fn(async () => ({
token: 't1',
user: { id: 'u1', email: null, displayName: 'D' },
user: { id: 'u1', email: null, displayName: 'John' },
})),
createSession: vi.fn(),
} as any,
@@ -76,7 +76,7 @@ describe('AuthService', () => {
await expect(service.getCurrentSession()).resolves.toEqual({
token: 't1',
user: { userId: 'u1', email: '', displayName: 'D' },
user: { userId: 'u1', email: '', displayName: 'John' },
});
});
@@ -89,7 +89,7 @@ describe('AuthService', () => {
const signupUseCase = {
execute: vi.fn(async () => {
authSessionPresenter.present({ userId: 'u2', email: 'e2', displayName: 'd2' });
authSessionPresenter.present({ userId: 'u2', email: 'e2', displayName: 'Jane Smith' });
return Result.ok(undefined);
}),
};
@@ -113,20 +113,20 @@ describe('AuthService', () => {
const session = await service.signupWithEmail({
email: 'e2',
password: 'p2',
displayName: 'd2',
displayName: 'Jane Smith',
} as any);
expect(signupUseCase.execute).toHaveBeenCalledWith({
email: 'e2',
password: 'p2',
displayName: 'd2',
displayName: 'Jane Smith',
});
expect(identitySessionPort.createSession).toHaveBeenCalledWith({
id: 'u2',
displayName: 'd2',
displayName: 'Jane Smith',
email: 'e2',
});
expect(session).toEqual({ token: 't2', user: { userId: 'u2', email: 'e2', displayName: 'd2' } });
expect(session).toEqual({ token: 't2', user: { userId: 'u2', email: 'e2', displayName: 'Jane Smith' } });
});
it('signupWithEmail throws with fallback when no details.message', async () => {
@@ -147,7 +147,7 @@ describe('AuthService', () => {
);
await expect(
service.signupWithEmail({ email: 'e2', password: 'p2', displayName: 'd2' } as any),
service.signupWithEmail({ email: 'e2', password: 'p2', displayName: 'Jane Smith' } as any),
).rejects.toThrow('Signup failed');
});
@@ -160,7 +160,7 @@ describe('AuthService', () => {
const loginUseCase = {
execute: vi.fn(async () => {
authSessionPresenter.present({ userId: 'u3', email: 'e3', displayName: 'd3' });
authSessionPresenter.present({ userId: 'u3', email: 'e3', displayName: 'Bob Wilson' });
return Result.ok(undefined);
}),
};
@@ -183,14 +183,14 @@ describe('AuthService', () => {
await expect(service.loginWithEmail({ email: 'e3', password: 'p3' } as any)).resolves.toEqual({
token: 't3',
user: { userId: 'u3', email: 'e3', displayName: 'd3' },
user: { userId: 'u3', email: 'e3', displayName: 'Bob Wilson' },
});
expect(loginUseCase.execute).toHaveBeenCalledWith({ email: 'e3', password: 'p3' });
expect(identitySessionPort.createSession).toHaveBeenCalledWith(
{
id: 'u3',
displayName: 'd3',
displayName: 'Bob Wilson',
email: 'e3',
},
undefined

View File

@@ -40,7 +40,7 @@ describe('Auth session (HTTP, inmemory)', () => {
const signupRes = await agent
.post('/auth/signup')
.send({ email: 'u1@gridpilot.local', password: 'pw1', displayName: 'User 1' })
.send({ email: 'u1@gridpilot.local', password: 'Password123!', displayName: 'John Smith' })
.expect(201);
const setCookie = signupRes.headers['set-cookie'] as string[] | undefined;
@@ -52,7 +52,7 @@ describe('Auth session (HTTP, inmemory)', () => {
token: expect.stringMatching(/^gp_/),
user: {
email: 'u1@gridpilot.local',
displayName: 'User 1',
displayName: 'John Smith',
userId: expect.any(String),
},
});
@@ -75,7 +75,7 @@ describe('Auth session (HTTP, inmemory)', () => {
user: {
userId: 'driver-1',
email: 'admin@gridpilot.local',
displayName: 'Admin',
displayName: 'Alex Martinez',
},
});

View File

@@ -14,7 +14,7 @@ describe('AuthSessionPresenter', () => {
it('maps user result into response model', () => {
const user = User.create({
id: UserId.fromString('user-1'),
displayName: 'Test User',
displayName: 'John Smith',
email: 'user@example.com',
passwordHash: PasswordHash.fromHash('hash'),
});
@@ -24,13 +24,13 @@ describe('AuthSessionPresenter', () => {
expect(presenter.getResponseModel()).toEqual({
userId: 'user-1',
email: 'user@example.com',
displayName: 'Test User',
displayName: 'John Smith',
});
expect(presenter.responseModel).toEqual({
userId: 'user-1',
email: 'user@example.com',
displayName: 'Test User',
displayName: 'John Smith',
});
});
@@ -42,7 +42,7 @@ describe('AuthSessionPresenter', () => {
it('reset clears model', () => {
const user = User.create({
id: UserId.fromString('user-1'),
displayName: 'Test User',
displayName: 'Jane Doe',
email: 'user@example.com',
passwordHash: PasswordHash.fromHash('hash'),
});

View File

@@ -22,8 +22,11 @@ describe('Racing seed (bootstrap)', () => {
expect(seed.sponsors.some((s) => s.id.toString() === 'demo-sponsor-1')).toBe(true);
// Seasons + sponsorship ecosystem
expect(seed.seasons.some((s) => s.id === 'season-1')).toBe(true);
expect(seed.seasons.some((s) => s.id === 'season-2')).toBe(true);
// Season IDs are generated as {leagueId}-season-{number}
// We just need to verify that seasons exist and have proper structure
expect(seed.seasons.length).toBeGreaterThan(0);
expect(seed.seasons.some((s) => s.id.includes('season-1'))).toBe(true);
expect(seed.seasons.some((s) => s.id.includes('season-2'))).toBe(true);
// Referential integrity: seasons must reference real leagues
for (const season of seed.seasons) {
@@ -56,10 +59,14 @@ describe('Racing seed (bootstrap)', () => {
}
}
const season1PendingRequests = seed.sponsorshipRequests.filter(
(r) => r.entityType === 'season' && r.entityId === 'season-1' && r.status === 'pending',
// Find a season that has pending sponsorship requests
const seasonWithPendingRequests = seed.sponsorshipRequests.find(
(r) => r.entityType === 'season' && r.status === 'pending',
);
expect(season1PendingRequests.length).toBeGreaterThan(0);
expect(seasonWithPendingRequests).toBeDefined();
if (seasonWithPendingRequests) {
expect(seasonById.has(seasonWithPendingRequests.entityId)).toBe(true);
}
// Wallet edge cases:
// - some leagues have no wallet at all

View File

@@ -88,7 +88,7 @@ describe('League roster admin read (HTTP, league-scoped)', () => {
await agent
.post('/auth/signup')
.send({ email: 'roster-read-user@gridpilot.local', password: 'pw1', displayName: 'Roster Read User' })
.send({ email: 'roster-read-user@gridpilot.local', password: 'Password123!', displayName: 'Roster Read User' })
.expect(201);
await agent.get('/leagues/league-5/admin/roster/members').expect(403);

View File

@@ -79,7 +79,7 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
it('rejects unauthenticated actor (401)', async () => {
await request(app.getHttpServer())
.post('/leagues/league-5/seasons/season-1/schedule/races')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/races')
.send({
track: 'Test Track',
car: 'Test Car',
@@ -93,11 +93,11 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
await agent
.post('/auth/signup')
.send({ email: 'user1@gridpilot.local', password: 'pw1', displayName: 'User 1' })
.send({ email: 'user1@gridpilot.local', password: 'Password123!', displayName: 'John Smith' })
.expect(201);
await agent
.post('/leagues/league-5/seasons/season-1/schedule/races')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/races')
.send({
track: 'Test Track',
car: 'Test Car',
@@ -133,13 +133,14 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
.send({ email: 'admin@gridpilot.local', password: 'admin123' })
.expect(201);
const initialScheduleRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
expect(initialScheduleRes.body).toMatchObject({ seasonId: 'season-1', races: expect.any(Array) });
const initialScheduleRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
expect(initialScheduleRes.body).toMatchObject({ seasonId: 'league-5-season-1', races: expect.any(Array) });
const scheduledAtIso = new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString();
// Try a date further in the future (100 days)
const scheduledAtIso = new Date(Date.now() + 100 * 24 * 60 * 60 * 1000).toISOString();
const createRes = await agent
.post('/leagues/league-5/seasons/season-1/schedule/races')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/races')
.send({
track: 'Test Track',
car: 'Test Car',
@@ -150,7 +151,7 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
expect(createRes.body).toMatchObject({ raceId: expect.any(String) });
const raceId: string = createRes.body.raceId;
const afterCreateRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const afterCreateRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
const createdRace = (afterCreateRes.body.races as any[]).find((r) => r.id === raceId);
expect(createdRace).toMatchObject({
id: raceId,
@@ -158,10 +159,10 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
date: scheduledAtIso,
});
const updatedAtIso = new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString();
const updatedAtIso = new Date(Date.now() + 105 * 24 * 60 * 60 * 1000).toISOString();
await agent
.patch(`/leagues/league-5/seasons/season-1/schedule/races/${raceId}`)
.patch(`/leagues/league-5/seasons/league-5-season-1/schedule/races/${raceId}`)
.send({
track: 'Updated Track',
car: 'Updated Car',
@@ -172,7 +173,7 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
expect(res.body).toEqual({ success: true });
});
const afterUpdateRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const afterUpdateRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
const updatedRace = (afterUpdateRes.body.races as any[]).find((r) => r.id === raceId);
expect(updatedRace).toMatchObject({
id: raceId,
@@ -180,11 +181,11 @@ describe('League schedule admin CRUD (HTTP, season-scoped)', () => {
date: updatedAtIso,
});
await agent.delete(`/leagues/league-5/seasons/season-1/schedule/races/${raceId}`).expect(200).expect({
await agent.delete(`/leagues/league-5/seasons/league-5-season-1/schedule/races/${raceId}`).expect(200).expect({
success: true,
});
const afterDeleteRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const afterDeleteRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
const deletedRace = (afterDeleteRes.body.races as any[]).find((r) => r.id === raceId);
expect(deletedRace).toBeUndefined();
});

View File

@@ -79,12 +79,12 @@ describe('League season schedule publish/unpublish (HTTP, season-scoped)', () =>
it('rejects unauthenticated actor (401)', async () => {
await request(app.getHttpServer())
.post('/leagues/league-5/seasons/season-1/schedule/publish')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/publish')
.send({})
.expect(401);
await request(app.getHttpServer())
.post('/leagues/league-5/seasons/season-1/schedule/unpublish')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/unpublish')
.send({})
.expect(401);
});
@@ -94,11 +94,11 @@ describe('League season schedule publish/unpublish (HTTP, season-scoped)', () =>
await agent
.post('/auth/signup')
.send({ email: 'user2@gridpilot.local', password: 'pw2', displayName: 'User 2' })
.send({ email: 'user2@gridpilot.local', password: 'Password123!', displayName: 'Jane Smith' })
.expect(201);
await agent.post('/leagues/league-5/seasons/season-1/schedule/publish').send({}).expect(403);
await agent.post('/leagues/league-5/seasons/season-1/schedule/unpublish').send({}).expect(403);
await agent.post('/leagues/league-5/seasons/league-5-season-1/schedule/publish').send({}).expect(403);
await agent.post('/leagues/league-5/seasons/league-5-season-1/schedule/unpublish').send({}).expect(403);
});
it('publish/unpublish toggles state and is reflected via schedule read (happy path)', async () => {
@@ -109,39 +109,39 @@ describe('League season schedule publish/unpublish (HTTP, season-scoped)', () =>
.send({ email: 'admin@gridpilot.local', password: 'admin123' })
.expect(201);
const initialScheduleRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const initialScheduleRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
expect(initialScheduleRes.body).toMatchObject({
seasonId: 'season-1',
seasonId: 'league-5-season-1',
published: false,
races: expect.any(Array),
});
await agent
.post('/leagues/league-5/seasons/season-1/schedule/publish')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/publish')
.send({})
.expect(200)
.expect((res) => {
expect(res.body).toEqual({ success: true, published: true });
});
const afterPublishRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const afterPublishRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
expect(afterPublishRes.body).toMatchObject({
seasonId: 'season-1',
seasonId: 'league-5-season-1',
published: true,
races: expect.any(Array),
});
await agent
.post('/leagues/league-5/seasons/season-1/schedule/unpublish')
.post('/leagues/league-5/seasons/league-5-season-1/schedule/unpublish')
.send({})
.expect(200)
.expect((res) => {
expect(res.body).toEqual({ success: true, published: false });
});
const afterUnpublishRes = await agent.get('/leagues/league-5/schedule?seasonId=season-1').expect(200);
const afterUnpublishRes = await agent.get('/leagues/league-5/schedule?seasonId=league-5-season-1').expect(200);
expect(afterUnpublishRes.body).toMatchObject({
seasonId: 'season-1',
seasonId: 'league-5-season-1',
published: false,
races: expect.any(Array),
});

View File

@@ -264,7 +264,7 @@ describe('MediaController', () => {
describe('getDefaultMedia', () => {
it('should return PNG with correct cache headers', async () => {
const variant = 'male-default-avatar';
const variant = 'logo';
const pngBuffer = Buffer.from([0x89, 0x50, 0x4E, 0x47]); // PNG header
generationService.generateDefaultPNG.mockReturnValue(pngBuffer);
@@ -280,7 +280,7 @@ describe('MediaController', () => {
});
it('should handle different variants', async () => {
const variants = ['male-default-avatar', 'female-default-avatar', 'neutral-default-avatar', 'logo'];
const variants = ['logo', 'other-variant', 'another-variant'];
for (const variant of variants) {
const pngBuffer = Buffer.from([0x89, 0x50, 0x4E, 0x47]);

View File

@@ -25,7 +25,7 @@ import { AUTH_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, USER_REPOSITORY_
id: 'driver-1',
email: 'admin@gridpilot.local',
passwordHash: 'demo_salt_321nimda', // InMemoryPasswordHashingService: "admin123" reversed.
displayName: 'Admin',
displayName: 'Alex Martinez',
createdAt: new Date(),
},
];

View File

@@ -277,7 +277,7 @@ describe('API Contract Validation', () => {
describe('Type Generation Integrity', () => {
it('should have valid TypeScript syntax in generated files', async () => {
const files = await fs.readdir(generatedTypesDir);
const dtos = files.filter(f => f.endsWith('.ts'));
const dtos = files.filter(f => f.endsWith('.ts') && !f.endsWith('.test.ts'));
for (const file of dtos) {
const content = await fs.readFile(path.join(generatedTypesDir, file), 'utf-8');
@@ -302,7 +302,7 @@ describe('API Contract Validation', () => {
it('should have proper imports for dependencies', async () => {
const files = await fs.readdir(generatedTypesDir);
const dtos = files.filter(f => f.endsWith('.ts'));
const dtos = files.filter(f => f.endsWith('.ts') && !f.endsWith('.test.ts'));
for (const file of dtos) {
const content = await fs.readFile(path.join(generatedTypesDir, file), 'utf-8');