view data fixes
Some checks failed
Contract Testing / contract-tests (pull_request) Failing after 7m11s
Contract Testing / contract-snapshot (pull_request) Has been skipped

This commit is contained in:
2026-01-24 23:29:55 +01:00
parent c1750a33dd
commit 1b0a1f4aee
134 changed files with 10380 additions and 415 deletions

View File

@@ -0,0 +1,145 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AdminDashboardPageQuery } from './AdminDashboardPageQuery';
import { AdminService } from '@/lib/services/admin/AdminService';
import { Result } from '@/lib/contracts/Result';
import { AdminDashboardViewDataBuilder } from '@/lib/builders/view-data/AdminDashboardViewDataBuilder';
import type { DashboardStats } from '@/lib/types/admin';
// Mock dependencies
vi.mock('@/lib/services/admin/AdminService', () => ({
AdminService: vi.fn(),
}));
vi.mock('@/lib/builders/view-data/AdminDashboardViewDataBuilder', () => ({
AdminDashboardViewDataBuilder: {
build: vi.fn(),
},
}));
describe('AdminDashboardPageQuery', () => {
let query: AdminDashboardPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new AdminDashboardPageQuery();
mockServiceInstance = {
getDashboardStats: vi.fn(),
};
vi.mocked(AdminService).mockImplementation(function() {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const mockStats: DashboardStats = {
totalUsers: 1250,
activeUsers: 1100,
suspendedUsers: 50,
deletedUsers: 100,
systemAdmins: 5,
recentLogins: 450,
newUsersToday: 12,
userGrowth: [
{ label: 'This week', value: 45, color: '#10b981' },
{ label: 'Last week', value: 38, color: '#3b82f6' },
],
roleDistribution: [
{ label: 'Users', value: 1200, color: '#6b7280' },
{ label: 'Admins', value: 50, color: '#8b5cf6' },
],
statusDistribution: {
active: 1100,
suspended: 50,
deleted: 100,
},
activityTimeline: [
{ date: '2024-01-01', newUsers: 10, logins: 200 },
{ date: '2024-01-02', newUsers: 15, logins: 220 },
],
};
const mockViewData = {
stats: {
totalUsers: 1250,
activeUsers: 1100,
suspendedUsers: 50,
deletedUsers: 100,
systemAdmins: 5,
recentLogins: 450,
newUsersToday: 12,
},
};
mockServiceInstance.getDashboardStats.mockResolvedValue(Result.ok(mockStats));
(AdminDashboardViewDataBuilder.build as any).mockReturnValue(mockViewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(mockViewData);
expect(AdminService).toHaveBeenCalled();
expect(mockServiceInstance.getDashboardStats).toHaveBeenCalled();
expect(AdminDashboardViewDataBuilder.build).toHaveBeenCalledWith(mockStats);
});
it('should return error when service fails', async () => {
const serviceError = { type: 'serverError', message: 'Service error' };
mockServiceInstance.getDashboardStats.mockResolvedValue(Result.err(serviceError));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const mockStats: DashboardStats = {
totalUsers: 1250,
activeUsers: 1100,
suspendedUsers: 50,
deletedUsers: 100,
systemAdmins: 5,
recentLogins: 450,
newUsersToday: 12,
userGrowth: [
{ label: 'This week', value: 45, color: '#10b981' },
{ label: 'Last week', value: 38, color: '#3b82f6' },
],
roleDistribution: [
{ label: 'Users', value: 1200, color: '#6b7280' },
{ label: 'Admins', value: 50, color: '#8b5cf6' },
],
statusDistribution: {
active: 1100,
suspended: 50,
deleted: 100,
},
activityTimeline: [
{ date: '2024-01-01', newUsers: 10, logins: 200 },
{ date: '2024-01-02', newUsers: 15, logins: 220 },
],
};
const mockViewData = {
stats: {
totalUsers: 1250,
activeUsers: 1100,
suspendedUsers: 50,
deletedUsers: 100,
systemAdmins: 5,
recentLogins: 450,
newUsersToday: 12,
},
};
mockServiceInstance.getDashboardStats.mockResolvedValue(Result.ok(mockStats));
(AdminDashboardViewDataBuilder.build as any).mockReturnValue(mockViewData);
const result = await AdminDashboardPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(mockViewData);
});
});

View File

@@ -0,0 +1,229 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AdminUsersPageQuery } from './AdminUsersPageQuery';
import { AdminService } from '@/lib/services/admin/AdminService';
import { Result } from '@/lib/contracts/Result';
import { AdminUsersViewDataBuilder } from '@/lib/builders/view-data/AdminUsersViewDataBuilder';
import type { UserListResponse } from '@/lib/types/admin';
// Mock dependencies
vi.mock('@/lib/services/admin/AdminService', () => ({
AdminService: vi.fn(),
}));
vi.mock('@/lib/builders/view-data/AdminUsersViewDataBuilder', () => ({
AdminUsersViewDataBuilder: {
build: vi.fn(),
},
}));
describe('AdminUsersPageQuery', () => {
let query: AdminUsersPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new AdminUsersPageQuery();
mockServiceInstance = {
listUsers: vi.fn(),
};
vi.mocked(AdminService).mockImplementation(function() {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const mockUsers: UserListResponse = {
users: [
{
id: '1',
email: 'admin@example.com',
displayName: 'Admin User',
roles: ['owner', 'admin'],
status: 'active',
isSystemAdmin: true,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
lastLoginAt: '2024-01-15T10:00:00.000Z',
primaryDriverId: 'driver-1',
},
{
id: '2',
email: 'user@example.com',
displayName: 'Regular User',
roles: ['user'],
status: 'active',
isSystemAdmin: false,
createdAt: '2024-01-02T00:00:00.000Z',
updatedAt: '2024-01-02T00:00:00.000Z',
lastLoginAt: '2024-01-14T15:00:00.000Z',
},
],
total: 2,
page: 1,
limit: 50,
totalPages: 1,
};
const mockViewData = {
users: [
{
id: '1',
email: 'admin@example.com',
displayName: 'Admin User',
roles: ['owner', 'admin'],
status: 'active',
isSystemAdmin: true,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
lastLoginAt: '2024-01-15T10:00:00.000Z',
primaryDriverId: 'driver-1',
},
{
id: '2',
email: 'user@example.com',
displayName: 'Regular User',
roles: ['user'],
status: 'active',
isSystemAdmin: false,
createdAt: '2024-01-02T00:00:00.000Z',
updatedAt: '2024-01-02T00:00:00.000Z',
lastLoginAt: '2024-01-14T15:00:00.000Z',
},
],
total: 2,
page: 1,
limit: 50,
totalPages: 1,
activeUserCount: 2,
adminCount: 1,
};
mockServiceInstance.listUsers.mockResolvedValue(Result.ok(mockUsers));
(AdminUsersViewDataBuilder.build as any).mockReturnValue(mockViewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(mockViewData);
expect(AdminService).toHaveBeenCalled();
expect(mockServiceInstance.listUsers).toHaveBeenCalled();
expect(AdminUsersViewDataBuilder.build).toHaveBeenCalledWith(mockUsers);
});
it('should return error when service fails', async () => {
const serviceError = { type: 'serverError', message: 'Service error' };
mockServiceInstance.listUsers.mockResolvedValue(Result.err(serviceError));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should return notFound error on 403 exception', async () => {
const error = new Error('403 Forbidden');
mockServiceInstance.listUsers.mockRejectedValue(error);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return notFound error on 401 exception', async () => {
const error = new Error('401 Unauthorized');
mockServiceInstance.listUsers.mockRejectedValue(error);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return serverError on other exceptions', async () => {
const error = new Error('Unexpected error');
mockServiceInstance.listUsers.mockRejectedValue(error);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const mockUsers: UserListResponse = {
users: [
{
id: '1',
email: 'admin@example.com',
displayName: 'Admin User',
roles: ['owner', 'admin'],
status: 'active',
isSystemAdmin: true,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
lastLoginAt: '2024-01-15T10:00:00.000Z',
primaryDriverId: 'driver-1',
},
{
id: '2',
email: 'user@example.com',
displayName: 'Regular User',
roles: ['user'],
status: 'active',
isSystemAdmin: false,
createdAt: '2024-01-02T00:00:00.000Z',
updatedAt: '2024-01-02T00:00:00.000Z',
lastLoginAt: '2024-01-14T15:00:00.000Z',
},
],
total: 2,
page: 1,
limit: 50,
totalPages: 1,
};
const mockViewData = {
users: [
{
id: '1',
email: 'admin@example.com',
displayName: 'Admin User',
roles: ['owner', 'admin'],
status: 'active',
isSystemAdmin: true,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-01T00:00:00.000Z',
lastLoginAt: '2024-01-15T10:00:00.000Z',
primaryDriverId: 'driver-1',
},
{
id: '2',
email: 'user@example.com',
displayName: 'Regular User',
roles: ['user'],
status: 'active',
isSystemAdmin: false,
createdAt: '2024-01-02T00:00:00.000Z',
updatedAt: '2024-01-02T00:00:00.000Z',
lastLoginAt: '2024-01-14T15:00:00.000Z',
},
],
total: 2,
page: 1,
limit: 50,
totalPages: 1,
activeUserCount: 2,
adminCount: 1,
};
mockServiceInstance.listUsers.mockResolvedValue(Result.ok(mockUsers));
(AdminUsersViewDataBuilder.build as any).mockReturnValue(mockViewData);
const result = await AdminUsersPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(mockViewData);
});
});

View File

@@ -0,0 +1,102 @@
/* eslint-disable gridpilot-rules/page-query-filename */
/* eslint-disable gridpilot-rules/clean-error-handling */
/* eslint-disable gridpilot-rules/page-query-must-use-builders */
/* eslint-disable gridpilot-rules/single-export-per-file */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CreateLeaguePageQuery } from './CreateLeaguePageQuery';
import { LeaguesApiClient } from '@/lib/gateways/api/leagues/LeaguesApiClient';
// Mock dependencies
vi.mock('@/lib/gateways/api/leagues/LeaguesApiClient', () => {
return {
LeaguesApiClient: vi.fn(),
};
});
vi.mock('@/lib/infrastructure/logging/ConsoleErrorReporter');
vi.mock('@/lib/infrastructure/logging/ConsoleLogger');
describe('CreateLeaguePageQuery', () => {
let query: CreateLeaguePageQuery;
let mockApiClientInstance: { getScoringPresets: ReturnType<typeof vi.fn> };
beforeEach(() => {
vi.clearAllMocks();
query = new CreateLeaguePageQuery();
mockApiClientInstance = {
getScoringPresets: vi.fn(),
};
vi.mocked(LeaguesApiClient).mockImplementation(function() {
return mockApiClientInstance as unknown as LeaguesApiClient;
});
});
it('should return scoring presets on success', async () => {
const presets = [{ id: 'preset-1', name: 'Standard' }];
mockApiClientInstance.getScoringPresets.mockResolvedValue({ presets });
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual({
scoringPresets: presets,
});
expect(mockApiClientInstance.getScoringPresets).toHaveBeenCalled();
});
it('should return empty array if presets are missing in response', async () => {
mockApiClientInstance.getScoringPresets.mockResolvedValue({});
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual({
scoringPresets: [],
});
});
it('should return redirect error on 401/403', async () => {
mockApiClientInstance.getScoringPresets.mockRejectedValue(new Error('401 Unauthorized'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('redirect');
});
it('should return notFound error on 404', async () => {
mockApiClientInstance.getScoringPresets.mockRejectedValue(new Error('404 Not Found'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return CREATE_LEAGUE_FETCH_FAILED on 5xx or server error', async () => {
mockApiClientInstance.getScoringPresets.mockRejectedValue(new Error('500 Internal Server Error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('CREATE_LEAGUE_FETCH_FAILED');
});
it('should return UNKNOWN_ERROR for other errors', async () => {
mockApiClientInstance.getScoringPresets.mockRejectedValue(new Error('Something went wrong'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('UNKNOWN_ERROR');
});
it('should provide a static execute method', async () => {
mockApiClientInstance.getScoringPresets.mockResolvedValue({ presets: [] });
const result = await CreateLeaguePageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual({ scoringPresets: [] });
});
});

View File

@@ -1,6 +1,6 @@
import { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
import { Result } from '@/lib/contracts/Result';
import { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient';
import { LeaguesApiClient } from '@/lib/gateways/api/leagues/LeaguesApiClient';
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';

View File

@@ -0,0 +1,82 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DashboardPageQuery } from './DashboardPageQuery';
import { DashboardService } from '@/lib/services/analytics/DashboardService';
import { Result } from '@/lib/contracts/Result';
import { DashboardViewDataBuilder } from '@/lib/builders/view-data/DashboardViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/analytics/DashboardService', () => ({
DashboardService: vi.fn().mockImplementation(function (this: any) {
this.getDashboardOverview = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/DashboardViewDataBuilder', () => ({
DashboardViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('DashboardPageQuery', () => {
let query: DashboardPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new DashboardPageQuery();
mockServiceInstance = {
getDashboardOverview: vi.fn(),
};
(DashboardService as any).mockImplementation(function (this: any) {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { some: 'dashboard-data' };
const viewData = { transformed: 'dashboard-view' } as any;
mockServiceInstance.getDashboardOverview.mockResolvedValue(Result.ok(apiDto));
(DashboardViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(DashboardService).toHaveBeenCalled();
expect(mockServiceInstance.getDashboardOverview).toHaveBeenCalled();
expect(DashboardViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = 'some-domain-error';
const presentationError = 'some-presentation-error';
mockServiceInstance.getDashboardOverview.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const apiDto = { some: 'dashboard-data' };
const viewData = { transformed: 'dashboard-view' } as any;
mockServiceInstance.getDashboardOverview.mockResolvedValue(Result.ok(apiDto));
(DashboardViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await DashboardPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,105 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DriverProfilePageQuery } from './DriverProfilePageQuery';
import { DriverProfilePageService } from '@/lib/services/drivers/DriverProfilePageService';
import { Result } from '@/lib/contracts/Result';
import { DriverProfileViewDataBuilder } from '@/lib/builders/view-data/DriverProfileViewDataBuilder';
// Mock dependencies
vi.mock('@/lib/services/drivers/DriverProfilePageService', () => ({
DriverProfilePageService: vi.fn().mockImplementation(function() {
return { getDriverProfile: vi.fn() };
}),
}));
vi.mock('@/lib/builders/view-data/DriverProfileViewDataBuilder', () => ({
DriverProfileViewDataBuilder: {
build: vi.fn(),
},
}));
describe('DriverProfilePageQuery', () => {
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
getDriverProfile: vi.fn(),
};
(DriverProfilePageService as any).mockImplementation(function() { return mockServiceInstance; });
});
it('should return view data when driverId is provided and service succeeds', async () => {
const driverId = 'driver-123';
const apiDto = { id: driverId, name: 'Test Driver' };
const viewData = { id: driverId, name: 'Test Driver' };
mockServiceInstance.getDriverProfile.mockResolvedValue(Result.ok(apiDto));
(DriverProfileViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await DriverProfilePageQuery.execute(driverId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(DriverProfilePageService).toHaveBeenCalled();
expect(mockServiceInstance.getDriverProfile).toHaveBeenCalledWith(driverId);
expect(DriverProfileViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return NotFound error when driverId is null', async () => {
const result = await DriverProfilePageQuery.execute(null);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('NotFound');
});
it('should return NotFound error when driverId is empty string', async () => {
const result = await DriverProfilePageQuery.execute('');
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('NotFound');
});
it('should return NotFound error when service returns notFound', async () => {
const driverId = 'driver-123';
mockServiceInstance.getDriverProfile.mockResolvedValue(Result.err('notFound'));
const result = await DriverProfilePageQuery.execute(driverId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('NotFound');
});
it('should return Unauthorized error when service returns unauthorized', async () => {
const driverId = 'driver-123';
mockServiceInstance.getDriverProfile.mockResolvedValue(Result.err('unauthorized'));
const result = await DriverProfilePageQuery.execute(driverId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Unauthorized');
});
it('should return Error for other service errors', async () => {
const driverId = 'driver-123';
mockServiceInstance.getDriverProfile.mockResolvedValue(Result.err('serverError'));
const result = await DriverProfilePageQuery.execute(driverId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
it('should return Error on exception', async () => {
const driverId = 'driver-123';
mockServiceInstance.getDriverProfile.mockRejectedValue(new Error('Unexpected error'));
const result = await DriverProfilePageQuery.execute(driverId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
});

View File

@@ -0,0 +1,81 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DriverRankingsPageQuery } from './DriverRankingsPageQuery';
import { DriverRankingsService } from '@/lib/services/leaderboards/DriverRankingsService';
import { Result } from '@/lib/contracts/Result';
import { DriverRankingsViewDataBuilder } from '@/lib/builders/view-data/DriverRankingsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
const mockGetDriverRankings = vi.fn();
vi.mock('@/lib/services/leaderboards/DriverRankingsService', () => {
return {
DriverRankingsService: class {
getDriverRankings = mockGetDriverRankings;
},
};
});
vi.mock('@/lib/builders/view-data/DriverRankingsViewDataBuilder', () => ({
DriverRankingsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('DriverRankingsPageQuery', () => {
let query: DriverRankingsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
getDriverRankings: mockGetDriverRankings,
};
query = new DriverRankingsPageQuery(mockServiceInstance);
});
it('should return view data when service succeeds', async () => {
const apiDto = { drivers: [{ id: 'driver-1', name: 'Test Driver', points: 100 }] };
const viewData = { drivers: [{ id: 'driver-1', name: 'Test Driver', points: 100 }] };
mockServiceInstance.getDriverRankings.mockResolvedValue(Result.ok(apiDto));
(DriverRankingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(mockServiceInstance.getDriverRankings).toHaveBeenCalled();
expect(DriverRankingsViewDataBuilder.build).toHaveBeenCalledWith(apiDto.drivers);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getDriverRankings.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const apiDto = { drivers: [{ id: 'driver-1', name: 'Test Driver', points: 100 }] };
const viewData = { drivers: [{ id: 'driver-1', name: 'Test Driver', points: 100 }] };
mockServiceInstance.getDriverRankings.mockResolvedValue(Result.ok(apiDto));
(DriverRankingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await DriverRankingsPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -11,9 +11,15 @@ import { mapToPresentationError, type PresentationError } from '@/lib/contracts/
* No DI container usage - constructs dependencies explicitly
*/
export class DriverRankingsPageQuery implements PageQuery<DriverRankingsViewData, void, PresentationError> {
private readonly service: DriverRankingsService;
constructor(service?: DriverRankingsService) {
this.service = service || new DriverRankingsService();
}
async execute(): Promise<Result<DriverRankingsViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new DriverRankingsService();
const service = this.service;
// Fetch data using service
const serviceResult = await service.getDriverRankings();

View File

@@ -0,0 +1,77 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { DriversPageQuery } from './DriversPageQuery';
import { DriversPageService } from '@/lib/services/drivers/DriversPageService';
import { Result } from '@/lib/contracts/Result';
import { DriversViewDataBuilder } from '@/lib/builders/view-data/DriversViewDataBuilder';
// Mock dependencies
vi.mock('@/lib/services/drivers/DriversPageService', () => ({
DriversPageService: vi.fn().mockImplementation(function (this: any) {
this.getLeaderboard = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/DriversViewDataBuilder', () => ({
DriversViewDataBuilder: {
build: vi.fn(),
},
}));
describe('DriversPageQuery', () => {
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
getLeaderboard: vi.fn(),
};
(DriversPageService as any).mockImplementation(function (this: any) {
return mockServiceInstance;
});
});
describe('execute', () => {
it('should return view data when service succeeds', async () => {
const apiDto = { some: 'drivers-data' };
const viewData = { transformed: 'drivers-view' } as any;
mockServiceInstance.getLeaderboard.mockResolvedValue(Result.ok(apiDto));
(DriversViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await DriversPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(DriversPageService).toHaveBeenCalled();
expect(mockServiceInstance.getLeaderboard).toHaveBeenCalled();
expect(DriversViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return NotFound when service returns notFound error', async () => {
mockServiceInstance.getLeaderboard.mockResolvedValue(Result.err('notFound'));
const result = await DriversPageQuery.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('NotFound');
});
it('should return Error when service returns other error', async () => {
mockServiceInstance.getLeaderboard.mockResolvedValue(Result.err('some-other-error'));
const result = await DriversPageQuery.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
it('should return Error on exception', async () => {
mockServiceInstance.getLeaderboard.mockRejectedValue(new Error('Unexpected error'));
const result = await DriversPageQuery.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
});
});

View File

@@ -0,0 +1,104 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { HomePageQuery } from './HomePageQuery';
import { HomeService } from '@/lib/services/home/HomeService';
import { Result } from '@/lib/contracts/Result';
import { HomeViewDataBuilder } from '@/lib/builders/view-data/HomeViewDataBuilder';
// Mock dependencies
vi.mock('@/lib/services/home/HomeService', () => ({
HomeService: vi.fn().mockImplementation(function (this: any) {
this.getHomeData = vi.fn();
this.shouldRedirectToDashboard = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/HomeViewDataBuilder', () => ({
HomeViewDataBuilder: {
build: vi.fn(),
},
}));
describe('HomePageQuery', () => {
let query: HomePageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new HomePageQuery();
mockServiceInstance = {
getHomeData: vi.fn(),
shouldRedirectToDashboard: vi.fn(),
};
(HomeService as any).mockImplementation(function (this: any) {
return mockServiceInstance;
});
});
describe('execute', () => {
it('should return view data when service succeeds', async () => {
const apiDto = { some: 'data' };
const viewData = { transformed: 'data' } as any;
mockServiceInstance.getHomeData.mockResolvedValue(Result.ok(apiDto));
(HomeViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(HomeService).toHaveBeenCalled();
expect(mockServiceInstance.getHomeData).toHaveBeenCalled();
expect(HomeViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return error when service fails', async () => {
mockServiceInstance.getHomeData.mockResolvedValue(Result.err('Service Error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
it('should return error on exception', async () => {
mockServiceInstance.getHomeData.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Error');
});
it('should provide a static execute method', async () => {
const apiDto = { some: 'data' };
const viewData = { transformed: 'data' } as any;
mockServiceInstance.getHomeData.mockResolvedValue(Result.ok(apiDto));
(HomeViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await HomePageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});
describe('shouldRedirectToDashboard', () => {
it('should return true when service returns true', async () => {
mockServiceInstance.shouldRedirectToDashboard.mockResolvedValue(true);
const result = await HomePageQuery.shouldRedirectToDashboard();
expect(result).toBe(true);
expect(mockServiceInstance.shouldRedirectToDashboard).toHaveBeenCalled();
});
it('should return false when service returns false', async () => {
mockServiceInstance.shouldRedirectToDashboard.mockResolvedValue(false);
const result = await HomePageQuery.shouldRedirectToDashboard();
expect(result).toBe(false);
});
});
});

View File

@@ -0,0 +1,82 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeaderboardsPageQuery } from './LeaderboardsPageQuery';
import { LeaderboardsService } from '@/lib/services/leaderboards/LeaderboardsService';
import { Result } from '@/lib/contracts/Result';
import { LeaderboardsViewDataBuilder } from '@/lib/builders/view-data/LeaderboardsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leaderboards/LeaderboardsService', () => ({
LeaderboardsService: vi.fn().mockImplementation(function (this: any) {
this.getLeaderboards = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeaderboardsViewDataBuilder', () => ({
LeaderboardsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeaderboardsPageQuery', () => {
let query: LeaderboardsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeaderboardsPageQuery();
mockServiceInstance = {
getLeaderboards: vi.fn(),
};
(LeaderboardsService as any).mockImplementation(function (this: any) {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { some: 'leaderboard-data' };
const viewData = { transformed: 'leaderboard-view' } as any;
mockServiceInstance.getLeaderboards.mockResolvedValue(Result.ok(apiDto));
(LeaderboardsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeaderboardsService).toHaveBeenCalled();
expect(mockServiceInstance.getLeaderboards).toHaveBeenCalled();
expect(LeaderboardsViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = 'some-domain-error';
const presentationError = 'some-presentation-error';
mockServiceInstance.getLeaderboards.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const apiDto = { some: 'leaderboard-data' };
const viewData = { transformed: 'leaderboard-view' } as any;
mockServiceInstance.getLeaderboards.mockResolvedValue(Result.ok(apiDto));
(LeaderboardsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeaderboardsPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,76 @@
/* eslint-disable gridpilot-rules/page-query-filename, gridpilot-rules/page-query-must-use-builders, gridpilot-rules/single-export-per-file, @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueDetailPageQuery } from './LeagueDetailPageQuery';
import { LeagueService } from '@/lib/services/leagues/LeagueService';
import { Result } from '@/lib/contracts/Result';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueService', () => ({
LeagueService: vi.fn(class {
getLeagueDetailData = vi.fn();
}),
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueDetailPageQuery', () => {
let query: LeagueDetailPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueDetailPageQuery();
mockServiceInstance = {
getLeagueDetailData: vi.fn(),
};
(LeagueService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return league detail data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { id: leagueId, name: 'Test League' } as any;
mockServiceInstance.getLeagueDetailData.mockResolvedValue(Result.ok(apiDto));
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(apiDto);
expect(LeagueService).toHaveBeenCalled();
expect(mockServiceInstance.getLeagueDetailData).toHaveBeenCalledWith(leagueId);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getLeagueDetailData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { id: leagueId, name: 'Test League' } as any;
mockServiceInstance.getLeagueDetailData.mockResolvedValue(Result.ok(apiDto));
const result = await LeagueDetailPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(apiDto);
});
});
export {};

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueProtestDetailPageQuery } from './LeagueProtestDetailPageQuery';
import { ProtestDetailService } from '@/lib/services/leagues/ProtestDetailService';
import { Result } from '@/lib/contracts/Result';
import { ProtestDetailViewDataBuilder } from '@/lib/builders/view-data/ProtestDetailViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/ProtestDetailService', () => ({
ProtestDetailService: vi.fn().mockImplementation(function (this: any) {
this.getProtestDetail = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/ProtestDetailViewDataBuilder', () => ({
ProtestDetailViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueProtestDetailPageQuery', () => {
let query: LeagueProtestDetailPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueProtestDetailPageQuery();
mockServiceInstance = {
getProtestDetail: vi.fn(),
};
(ProtestDetailService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { leagueId: 'league-123', protestId: 'protest-456' };
const apiDto = { protest: { id: 'protest-456', description: 'Test protest' } };
const viewData = { protest: { id: 'protest-456', description: 'Test protest' } };
mockServiceInstance.getProtestDetail.mockResolvedValue(Result.ok(apiDto));
(ProtestDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(ProtestDetailService).toHaveBeenCalled();
expect(mockServiceInstance.getProtestDetail).toHaveBeenCalledWith('league-123', 'protest-456');
expect(ProtestDetailViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { leagueId: 'league-123', protestId: 'protest-456' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getProtestDetail.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const params = { leagueId: 'league-123', protestId: 'protest-456' };
const apiDto = { protest: { id: 'protest-456', description: 'Test protest' } };
const viewData = { protest: { id: 'protest-456', description: 'Test protest' } };
mockServiceInstance.getProtestDetail.mockResolvedValue(Result.ok(apiDto));
(ProtestDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueProtestDetailPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,104 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueProtestReviewPageQuery } from './LeagueProtestReviewPageQuery';
import { Result } from '@/lib/contracts/Result';
import { LeaguesApiClient } from '@/lib/gateways/api/leagues/LeaguesApiClient';
import { ProtestsApiClient } from '@/lib/gateways/api/protests/ProtestsApiClient';
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
// Mock dependencies
vi.mock('@/lib/gateways/api/leagues/LeaguesApiClient', () => ({
LeaguesApiClient: vi.fn().mockImplementation(function (this: any) { return this; }),
}));
vi.mock('@/lib/gateways/api/protests/ProtestsApiClient', () => ({
ProtestsApiClient: vi.fn().mockImplementation(function (this: any) { return this; }),
}));
vi.mock('@/lib/infrastructure/logging/ConsoleErrorReporter', () => ({
ConsoleErrorReporter: vi.fn().mockImplementation(function (this: any) { return this; }),
}));
vi.mock('@/lib/infrastructure/logging/ConsoleLogger', () => ({
ConsoleLogger: vi.fn().mockImplementation(function (this: any) { return this; }),
}));
describe('LeagueProtestReviewPageQuery', () => {
let query: LeagueProtestReviewPageQuery;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueProtestReviewPageQuery();
});
it('should return placeholder data when execution succeeds', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
const result = await query.execute(input);
expect(result.isOk()).toBe(true);
const data = result.unwrap() as any;
expect(data.protest.id).toBe('protest-456');
expect(LeaguesApiClient).toHaveBeenCalled();
expect(ProtestsApiClient).toHaveBeenCalled();
expect(ConsoleErrorReporter).toHaveBeenCalled();
expect(ConsoleLogger).toHaveBeenCalled();
});
it('should return redirect error on 403/401 errors', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
(LeaguesApiClient as any).mockImplementationOnce(function () {
throw new Error('403 Forbidden');
});
const result = await query.execute(input);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('redirect');
});
it('should return notFound error on 404 errors', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
(LeaguesApiClient as any).mockImplementationOnce(function () {
throw new Error('404 Not Found');
});
const result = await query.execute(input);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return PROTEST_FETCH_FAILED on server errors', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
(LeaguesApiClient as any).mockImplementationOnce(function () {
throw new Error('500 Internal Server Error');
});
const result = await query.execute(input);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('PROTEST_FETCH_FAILED');
});
it('should return UNKNOWN_ERROR on other errors', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
(LeaguesApiClient as any).mockImplementationOnce(function () {
throw new Error('Some random error');
});
const result = await query.execute(input);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('UNKNOWN_ERROR');
});
it('should provide a static execute method', async () => {
const input = { leagueId: 'league-123', protestId: 'protest-456' };
const result = await LeagueProtestReviewPageQuery.execute(input);
expect(result.isOk()).toBe(true);
expect((result.unwrap() as any).protest.id).toBe('protest-456');
});
});

View File

@@ -1,7 +1,7 @@
import { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
import { Result } from '@/lib/contracts/Result';
import { LeaguesApiClient } from '@/lib/api/leagues/LeaguesApiClient';
import { ProtestsApiClient } from '@/lib/api/protests/ProtestsApiClient';
import { LeaguesApiClient } from '@/lib/gateways/api/leagues/LeaguesApiClient';
import { ProtestsApiClient } from '@/lib/gateways/api/protests/ProtestsApiClient';
import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorReporter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
@@ -12,14 +12,13 @@ import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
*/
export class LeagueProtestReviewPageQuery implements PageQuery<unknown, { leagueId: string; protestId: string }> {
async execute(input: { leagueId: string; protestId: string }): Promise<Result<unknown, 'notFound' | 'redirect' | 'PROTEST_FETCH_FAILED' | 'UNKNOWN_ERROR'>> {
// Manual wiring: create API clients
const baseUrl = process.env.NEXT_PUBLIC_API_URL || '';
const errorReporter = new ConsoleErrorReporter();
const logger = new ConsoleLogger();
new LeaguesApiClient(baseUrl, errorReporter, logger);
new ProtestsApiClient(baseUrl, errorReporter, logger);
try {
// Manual wiring: create API clients
const baseUrl = process.env.NEXT_PUBLIC_API_URL || '';
const errorReporter = new ConsoleErrorReporter();
const logger = new ConsoleLogger();
new LeaguesApiClient(baseUrl, errorReporter, logger);
new ProtestsApiClient(baseUrl, errorReporter, logger);
// Get protest details
// Note: This would need a getProtestDetail method on ProtestsApiClient
// For now, return placeholder data

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueRosterAdminPageQuery } from './LeagueRosterAdminPageQuery';
import { LeagueService } from '@/lib/services/leagues/LeagueService';
import { Result } from '@/lib/contracts/Result';
import { LeagueRosterAdminViewDataBuilder } from '@/lib/builders/view-data/LeagueRosterAdminViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueService', () => ({
LeagueService: vi.fn(class {
getRosterAdminData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueRosterAdminViewDataBuilder', () => ({
LeagueRosterAdminViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueRosterAdminPageQuery', () => {
let query: LeagueRosterAdminPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueRosterAdminPageQuery();
mockServiceInstance = {
getRosterAdminData: vi.fn(),
};
(LeagueService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { members: [], joinRequests: [] };
const viewData = { members: [], joinRequests: [] };
mockServiceInstance.getRosterAdminData.mockResolvedValue(Result.ok(apiDto));
(LeagueRosterAdminViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueService).toHaveBeenCalled();
expect(mockServiceInstance.getRosterAdminData).toHaveBeenCalledWith(leagueId);
expect(LeagueRosterAdminViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRosterAdminData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { members: [], joinRequests: [] };
const viewData = { members: [], joinRequests: [] };
mockServiceInstance.getRosterAdminData.mockResolvedValue(Result.ok(apiDto));
(LeagueRosterAdminViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueRosterAdminPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueRulebookPageQuery } from './LeagueRulebookPageQuery';
import { LeagueRulebookService } from '@/lib/services/leagues/LeagueRulebookService';
import { Result } from '@/lib/contracts/Result';
import { RulebookViewDataBuilder } from '@/lib/builders/view-data/RulebookViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueRulebookService', () => ({
LeagueRulebookService: vi.fn().mockImplementation(function (this: any) {
this.getRulebookData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RulebookViewDataBuilder', () => ({
RulebookViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueRulebookPageQuery', () => {
let query: LeagueRulebookPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueRulebookPageQuery();
mockServiceInstance = {
getRulebookData: vi.fn(),
};
(LeagueRulebookService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { rulebook: 'rules' };
const viewData = { rulebook: 'rules' };
mockServiceInstance.getRulebookData.mockResolvedValue(Result.ok(apiDto));
(RulebookViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueRulebookService).toHaveBeenCalled();
expect(mockServiceInstance.getRulebookData).toHaveBeenCalledWith(leagueId);
expect(RulebookViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRulebookData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { rulebook: 'rules' };
const viewData = { rulebook: 'rules' };
mockServiceInstance.getRulebookData.mockResolvedValue(Result.ok(apiDto));
(RulebookViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueRulebookPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,131 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueScheduleAdminPageQuery } from './LeagueScheduleAdminPageQuery';
import { LeagueService } from '@/lib/services/leagues/LeagueService';
import { Result } from '@/lib/contracts/Result';
import { LeagueScheduleViewDataBuilder } from '@/lib/builders/view-data/LeagueScheduleViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueService', () => ({
LeagueService: vi.fn(class {
getScheduleAdminData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueScheduleViewDataBuilder', () => ({
LeagueScheduleViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueScheduleAdminPageQuery', () => {
let query: LeagueScheduleAdminPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueScheduleAdminPageQuery();
mockServiceInstance = {
getScheduleAdminData: vi.fn(),
};
(LeagueService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const input = { leagueId: 'league-123', seasonId: 'season-456' };
const apiDto = {
leagueId: 'league-123',
schedule: {
races: [
{
id: 'race-1',
name: 'Race 1',
scheduledAt: '2024-01-01',
track: 'Track 1',
car: 'Car 1',
sessionType: 'Practice',
},
],
},
};
const viewData = { leagueId: 'league-123', races: [] };
mockServiceInstance.getScheduleAdminData.mockResolvedValue(Result.ok(apiDto));
(LeagueScheduleViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueService).toHaveBeenCalled();
expect(mockServiceInstance.getScheduleAdminData).toHaveBeenCalledWith(input.leagueId, input.seasonId);
expect(LeagueScheduleViewDataBuilder.build).toHaveBeenCalledWith({
leagueId: apiDto.leagueId,
races: [
{
id: 'race-1',
name: 'Race 1',
date: '2024-01-01',
track: 'Track 1',
car: 'Car 1',
sessionType: 'Practice',
},
],
});
});
it('should return view data when seasonId is optional', async () => {
const input = { leagueId: 'league-123' };
const apiDto = {
leagueId: 'league-123',
schedule: { races: [] },
};
const viewData = { leagueId: 'league-123', races: [] };
mockServiceInstance.getScheduleAdminData.mockResolvedValue(Result.ok(apiDto));
(LeagueScheduleViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(input);
expect(result.isOk()).toBe(true);
expect(mockServiceInstance.getScheduleAdminData).toHaveBeenCalledWith(input.leagueId, undefined);
});
it('should return mapped presentation error when service fails', async () => {
const input = { leagueId: 'league-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getScheduleAdminData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(input);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const input = { leagueId: 'league-123' };
const apiDto = {
leagueId: 'league-123',
schedule: { races: [] },
};
const viewData = { leagueId: 'league-123', races: [] };
mockServiceInstance.getScheduleAdminData.mockResolvedValue(Result.ok(apiDto));
(LeagueScheduleViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueScheduleAdminPageQuery.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueSchedulePageQuery } from './LeagueSchedulePageQuery';
import { LeagueScheduleService } from '@/lib/services/leagues/LeagueScheduleService';
import { Result } from '@/lib/contracts/Result';
import { LeagueScheduleViewDataBuilder } from '@/lib/builders/view-data/LeagueScheduleViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueScheduleService', () => ({
LeagueScheduleService: vi.fn(class {
getScheduleData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueScheduleViewDataBuilder', () => ({
LeagueScheduleViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueSchedulePageQuery', () => {
let query: LeagueSchedulePageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueSchedulePageQuery();
mockServiceInstance = {
getScheduleData: vi.fn(),
};
(LeagueScheduleService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { races: [] };
const viewData = { races: [] };
mockServiceInstance.getScheduleData.mockResolvedValue(Result.ok(apiDto));
(LeagueScheduleViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueScheduleService).toHaveBeenCalled();
expect(mockServiceInstance.getScheduleData).toHaveBeenCalledWith(leagueId);
expect(LeagueScheduleViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getScheduleData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { races: [] };
const viewData = { races: [] };
mockServiceInstance.getScheduleData.mockResolvedValue(Result.ok(apiDto));
(LeagueScheduleViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueSchedulePageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueSettingsPageQuery } from './LeagueSettingsPageQuery';
import { LeagueSettingsService } from '@/lib/services/leagues/LeagueSettingsService';
import { Result } from '@/lib/contracts/Result';
import { LeagueSettingsViewDataBuilder } from '@/lib/builders/view-data/LeagueSettingsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueSettingsService', () => ({
LeagueSettingsService: vi.fn().mockImplementation(function (this: any) {
this.getSettingsData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueSettingsViewDataBuilder', () => ({
LeagueSettingsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueSettingsPageQuery', () => {
let query: LeagueSettingsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueSettingsPageQuery();
mockServiceInstance = {
getSettingsData: vi.fn(),
};
(LeagueSettingsService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { id: leagueId, name: 'Test League' };
const viewData = { id: leagueId, name: 'Test League' };
mockServiceInstance.getSettingsData.mockResolvedValue(Result.ok(apiDto));
(LeagueSettingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueSettingsService).toHaveBeenCalled();
expect(mockServiceInstance.getSettingsData).toHaveBeenCalledWith(leagueId);
expect(LeagueSettingsViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getSettingsData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { id: leagueId, name: 'Test League' };
const viewData = { id: leagueId, name: 'Test League' };
mockServiceInstance.getSettingsData.mockResolvedValue(Result.ok(apiDto));
(LeagueSettingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueSettingsPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueSponsorshipsPageQuery } from './LeagueSponsorshipsPageQuery';
import { LeagueSponsorshipsService } from '@/lib/services/leagues/LeagueSponsorshipsService';
import { Result } from '@/lib/contracts/Result';
import { LeagueSponsorshipsViewDataBuilder } from '@/lib/builders/view-data/LeagueSponsorshipsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueSponsorshipsService', () => ({
LeagueSponsorshipsService: vi.fn().mockImplementation(function (this: any) {
this.getSponsorshipsData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueSponsorshipsViewDataBuilder', () => ({
LeagueSponsorshipsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueSponsorshipsPageQuery', () => {
let query: LeagueSponsorshipsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueSponsorshipsPageQuery();
mockServiceInstance = {
getSponsorshipsData: vi.fn(),
};
(LeagueSponsorshipsService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { sponsorships: [] };
const viewData = { sponsorships: [] };
mockServiceInstance.getSponsorshipsData.mockResolvedValue(Result.ok(apiDto));
(LeagueSponsorshipsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueSponsorshipsService).toHaveBeenCalled();
expect(mockServiceInstance.getSponsorshipsData).toHaveBeenCalledWith(leagueId);
expect(LeagueSponsorshipsViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getSponsorshipsData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { sponsorships: [] };
const viewData = { sponsorships: [] };
mockServiceInstance.getSponsorshipsData.mockResolvedValue(Result.ok(apiDto));
(LeagueSponsorshipsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueSponsorshipsPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueStandingsPageQuery } from './LeagueStandingsPageQuery';
import { LeagueStandingsService } from '@/lib/services/leagues/LeagueStandingsService';
import { Result } from '@/lib/contracts/Result';
import { LeagueStandingsViewDataBuilder } from '@/lib/builders/view-data/LeagueStandingsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueStandingsService', () => ({
LeagueStandingsService: vi.fn().mockImplementation(function (this: any) {
this.getStandingsData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueStandingsViewDataBuilder', () => ({
LeagueStandingsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueStandingsPageQuery', () => {
let query: LeagueStandingsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueStandingsPageQuery();
mockServiceInstance = {
getStandingsData: vi.fn(),
};
(LeagueStandingsService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { standings: [], memberships: [] };
const viewData = { standings: [], memberships: [] };
mockServiceInstance.getStandingsData.mockResolvedValue(Result.ok(apiDto));
(LeagueStandingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueStandingsService).toHaveBeenCalled();
expect(mockServiceInstance.getStandingsData).toHaveBeenCalledWith(leagueId);
expect(LeagueStandingsViewDataBuilder.build).toHaveBeenCalledWith(apiDto.standings, apiDto.memberships, leagueId);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getStandingsData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { standings: [], memberships: [] };
const viewData = { standings: [], memberships: [] };
mockServiceInstance.getStandingsData.mockResolvedValue(Result.ok(apiDto));
(LeagueStandingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueStandingsPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueStewardingPageQuery } from './LeagueStewardingPageQuery';
import { LeagueStewardingService } from '@/lib/services/leagues/LeagueStewardingService';
import { Result } from '@/lib/contracts/Result';
import { StewardingViewDataBuilder } from '@/lib/builders/view-data/StewardingViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueStewardingService', () => ({
LeagueStewardingService: vi.fn().mockImplementation(function (this: any) {
this.getStewardingData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/StewardingViewDataBuilder', () => ({
StewardingViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueStewardingPageQuery', () => {
let query: LeagueStewardingPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueStewardingPageQuery();
mockServiceInstance = {
getStewardingData: vi.fn(),
};
(LeagueStewardingService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { stewarding: { incidents: [] } };
const viewData = { stewarding: { incidents: [] } };
mockServiceInstance.getStewardingData.mockResolvedValue(Result.ok(apiDto));
(StewardingViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueStewardingService).toHaveBeenCalled();
expect(mockServiceInstance.getStewardingData).toHaveBeenCalledWith(leagueId);
expect(StewardingViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getStewardingData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { stewarding: { incidents: [] } };
const viewData = { stewarding: { incidents: [] } };
mockServiceInstance.getStewardingData.mockResolvedValue(Result.ok(apiDto));
(StewardingViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueStewardingPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,85 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeagueWalletPageQuery } from './LeagueWalletPageQuery';
import { LeagueWalletService } from '@/lib/services/leagues/LeagueWalletService';
import { Result } from '@/lib/contracts/Result';
import { LeagueWalletViewDataBuilder } from '@/lib/builders/view-data/LeagueWalletViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueWalletService', () => ({
LeagueWalletService: vi.fn(class {
getWalletData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueWalletViewDataBuilder', () => ({
LeagueWalletViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('LeagueWalletPageQuery', () => {
let query: LeagueWalletPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeagueWalletPageQuery();
mockServiceInstance = {
getWalletData: vi.fn(),
};
(LeagueWalletService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const leagueId = 'league-123';
const apiDto = { balance: 100 };
const viewData = { balance: 100 };
mockServiceInstance.getWalletData.mockResolvedValue(Result.ok(apiDto));
(LeagueWalletViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueWalletService).toHaveBeenCalled();
expect(mockServiceInstance.getWalletData).toHaveBeenCalledWith(leagueId);
expect(LeagueWalletViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const leagueId = 'league-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getWalletData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(leagueId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const leagueId = 'league-123';
const apiDto = { balance: 100 };
const viewData = { balance: 100 };
mockServiceInstance.getWalletData.mockResolvedValue(Result.ok(apiDto));
(LeagueWalletViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeagueWalletPageQuery.execute(leagueId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,105 @@
/* eslint-disable gridpilot-rules/page-query-filename, gridpilot-rules/single-export-per-file, @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LeaguesPageQuery } from './LeaguesPageQuery';
import { LeagueService } from '@/lib/services/leagues/LeagueService';
import { Result } from '@/lib/contracts/Result';
import { LeaguesViewDataBuilder } from '@/lib/builders/view-data/LeaguesViewDataBuilder';
// Mock dependencies
vi.mock('@/lib/services/leagues/LeagueService', () => ({
LeagueService: vi.fn(class {
getAllLeagues = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeaguesViewDataBuilder', () => ({
LeaguesViewDataBuilder: {
build: vi.fn(),
},
}));
describe('LeaguesPageQuery', () => {
let query: LeaguesPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new LeaguesPageQuery();
mockServiceInstance = {
getAllLeagues: vi.fn(),
};
(LeagueService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = [{ id: 'league-1', name: 'League 1' }] as any;
const viewData = { leagues: apiDto } as any;
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.ok(apiDto));
(LeaguesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(LeagueService).toHaveBeenCalled();
expect(mockServiceInstance.getAllLeagues).toHaveBeenCalled();
expect(LeaguesViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return notFound when service returns notFound', async () => {
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.err({ type: 'notFound' }));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return redirect when service returns unauthorized or forbidden', async () => {
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.err({ type: 'unauthorized' }));
let result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('redirect');
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.err({ type: 'forbidden' }));
result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('redirect');
});
it('should return LEAGUES_FETCH_FAILED when service returns serverError', async () => {
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.err({ type: 'serverError' }));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('LEAGUES_FETCH_FAILED');
});
it('should return UNKNOWN_ERROR for other errors', async () => {
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.err({ type: 'other' }));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('UNKNOWN_ERROR');
});
it('should provide a static execute method', async () => {
const apiDto = [] as any;
const viewData = { leagues: [] } as any;
mockServiceInstance.getAllLeagues.mockResolvedValue(Result.ok(apiDto));
(LeaguesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LeaguesPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});
export {};

View File

@@ -1,3 +1,5 @@
/* eslint-disable gridpilot-rules/page-query-filename */
/* eslint-disable gridpilot-rules/single-export-per-file */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { OnboardingPageQuery } from './OnboardingPageQuery';
import { OnboardingService } from '@/lib/services/onboarding/OnboardingService';
@@ -19,7 +21,7 @@ vi.mock('@/lib/builders/view-data/OnboardingPageViewDataBuilder', () => ({
describe('OnboardingPageQuery', () => {
let query: OnboardingPageQuery;
let mockServiceInstance: any;
let mockServiceInstance: { checkCurrentDriver: ReturnType<typeof vi.fn> };
beforeEach(() => {
vi.clearAllMocks();
@@ -28,17 +30,17 @@ describe('OnboardingPageQuery', () => {
checkCurrentDriver: vi.fn(),
};
// Use mockImplementation to return the instance
(OnboardingService as any).mockImplementation(function() {
return mockServiceInstance;
vi.mocked(OnboardingService).mockImplementation(function() {
return mockServiceInstance as unknown as OnboardingService;
});
});
it('should return view data with isAlreadyOnboarded: true when driver exists', async () => {
const driver = { id: 'driver-1' };
const viewData = { isAlreadyOnboarded: true };
const viewData = { isAlreadyOnboarded: true } as unknown as ReturnType<typeof OnboardingPageViewDataBuilder.build>;
mockServiceInstance.checkCurrentDriver.mockResolvedValue(Result.ok(driver));
(OnboardingPageViewDataBuilder.build as any).mockReturnValue(viewData);
vi.mocked(OnboardingPageViewDataBuilder.build).mockReturnValue(viewData);
const result = await query.execute();
@@ -48,10 +50,10 @@ describe('OnboardingPageQuery', () => {
});
it('should return view data with isAlreadyOnboarded: false when driver not found', async () => {
const viewData = { isAlreadyOnboarded: false };
const viewData = { isAlreadyOnboarded: false } as unknown as ReturnType<typeof OnboardingPageViewDataBuilder.build>;
mockServiceInstance.checkCurrentDriver.mockResolvedValue(Result.err({ type: 'notFound' }));
(OnboardingPageViewDataBuilder.build as any).mockReturnValue(viewData);
vi.mocked(OnboardingPageViewDataBuilder.build).mockReturnValue(viewData);
const result = await query.execute();
@@ -79,9 +81,9 @@ describe('OnboardingPageQuery', () => {
});
it('should provide a static execute method', async () => {
const viewData = { isAlreadyOnboarded: true };
const viewData = { isAlreadyOnboarded: true } as unknown as ReturnType<typeof OnboardingPageViewDataBuilder.build>;
mockServiceInstance.checkCurrentDriver.mockResolvedValue(Result.ok({}));
(OnboardingPageViewDataBuilder.build as any).mockReturnValue(viewData);
vi.mocked(OnboardingPageViewDataBuilder.build).mockReturnValue(viewData);
const result = await OnboardingPageQuery.execute();

View File

@@ -0,0 +1,117 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProfileLeaguesPageQuery } from './ProfileLeaguesPageQuery';
import { SessionGateway } from '@/lib/gateways/SessionGateway';
import { ProfileLeaguesService } from '@/lib/services/leagues/ProfileLeaguesService';
import { ProfileLeaguesViewDataBuilder } from '@/lib/builders/view-data/ProfileLeaguesViewDataBuilder';
import { Result } from '@/lib/contracts/Result';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/gateways/SessionGateway', () => ({
SessionGateway: vi.fn().mockImplementation(function() {
return { getSession: vi.fn() };
}),
}));
vi.mock('@/lib/services/leagues/ProfileLeaguesService', () => ({
ProfileLeaguesService: vi.fn().mockImplementation(function() {
return { getProfileLeagues: vi.fn() };
}),
}));
vi.mock('@/lib/builders/view-data/ProfileLeaguesViewDataBuilder', () => ({
ProfileLeaguesViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('ProfileLeaguesPageQuery', () => {
let query: ProfileLeaguesPageQuery;
let mockSessionGateway: any;
let mockService: any;
beforeEach(() => {
vi.clearAllMocks();
query = new ProfileLeaguesPageQuery();
mockSessionGateway = {
getSession: vi.fn(),
};
(SessionGateway as any).mockImplementation(function() { return mockSessionGateway; });
mockService = {
getProfileLeagues: vi.fn(),
};
(ProfileLeaguesService as any).mockImplementation(function() { return mockService; });
});
it('should return notFound if no session exists', async () => {
mockSessionGateway.getSession.mockResolvedValue(null);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return notFound if session has no primaryDriverId', async () => {
mockSessionGateway.getSession.mockResolvedValue({ user: {} });
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return view data when service succeeds', async () => {
const driverId = 'driver-123';
const apiDto = { leagues: [] };
const viewData = { leagues: [] };
mockSessionGateway.getSession.mockResolvedValue({ user: { primaryDriverId: driverId } });
mockService.getProfileLeagues.mockResolvedValue(Result.ok(apiDto));
(ProfileLeaguesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(mockService.getProfileLeagues).toHaveBeenCalledWith(driverId);
expect(ProfileLeaguesViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const driverId = 'driver-123';
const serviceError = 'someError';
const presentationError = 'serverError';
mockSessionGateway.getSession.mockResolvedValue({ user: { primaryDriverId: driverId } });
mockService.getProfileLeagues.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const driverId = 'driver-123';
const apiDto = { leagues: [] };
const viewData = { leagues: [] };
mockSessionGateway.getSession.mockResolvedValue({ user: { primaryDriverId: driverId } });
mockService.getProfileLeagues.mockResolvedValue(Result.ok(apiDto));
(ProfileLeaguesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await ProfileLeaguesPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,100 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ProfilePageQuery } from './ProfilePageQuery';
import { SessionGateway } from '@/lib/gateways/SessionGateway';
import { DriverProfileService } from '@/lib/services/drivers/DriverProfileService';
import { ProfileViewDataBuilder } from '@/lib/builders/view-data/ProfileViewDataBuilder';
import { Result } from '@/lib/contracts/Result';
// Mock dependencies
vi.mock('@/lib/gateways/SessionGateway', () => ({
SessionGateway: vi.fn().mockImplementation(function() {
return { getSession: vi.fn() };
}),
}));
vi.mock('@/lib/services/drivers/DriverProfileService', () => ({
DriverProfileService: vi.fn().mockImplementation(function() {
return { getDriverProfile: vi.fn() };
}),
}));
vi.mock('@/lib/builders/view-data/ProfileViewDataBuilder', () => ({
ProfileViewDataBuilder: {
build: vi.fn(),
},
}));
describe('ProfilePageQuery', () => {
let query: ProfilePageQuery;
let mockSessionGateway: any;
let mockService: any;
beforeEach(() => {
vi.clearAllMocks();
query = new ProfilePageQuery();
mockSessionGateway = {
getSession: vi.fn(),
};
(SessionGateway as any).mockImplementation(function() { return mockSessionGateway; });
mockService = {
getDriverProfile: vi.fn(),
};
(DriverProfileService as any).mockImplementation(function() { return mockService; });
});
it('should return notFound if no session exists', async () => {
mockSessionGateway.getSession.mockResolvedValue(null);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return notFound if session has no primaryDriverId', async () => {
mockSessionGateway.getSession.mockResolvedValue({ user: {} });
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return view data when service succeeds', async () => {
const driverId = 'driver-123';
const apiDto = { id: driverId, name: 'Test Driver' };
const viewData = { id: driverId, name: 'Test Driver' };
mockSessionGateway.getSession.mockResolvedValue({ user: { primaryDriverId: driverId } });
mockService.getDriverProfile.mockResolvedValue(Result.ok(apiDto));
(ProfileViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(mockService.getDriverProfile).toHaveBeenCalledWith(driverId);
expect(ProfileViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should map service errors correctly', async () => {
const driverId = 'driver-123';
mockSessionGateway.getSession.mockResolvedValue({ user: { primaryDriverId: driverId } });
const errorMappings = [
{ service: 'notFound', expected: 'notFound' },
{ service: 'unauthorized', expected: 'unauthorized' },
{ service: 'serverError', expected: 'serverError' },
{ service: 'other', expected: 'unknown' },
];
for (const mapping of errorMappings) {
mockService.getDriverProfile.mockResolvedValue(Result.err(mapping.service));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(mapping.expected);
}
});
});

View File

@@ -0,0 +1,83 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SponsorDashboardPageQuery } from './SponsorDashboardPageQuery';
import { SponsorService } from '@/lib/services/sponsors/SponsorService';
import { Result } from '@/lib/contracts/Result';
import { SponsorDashboardViewDataBuilder } from '@/lib/builders/view-data/SponsorDashboardViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/sponsors/SponsorService', () => ({
SponsorService: vi.fn(),
}));
vi.mock('@/lib/builders/view-data/SponsorDashboardViewDataBuilder', () => ({
SponsorDashboardViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('SponsorDashboardPageQuery', () => {
let query: SponsorDashboardPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new SponsorDashboardPageQuery();
mockServiceInstance = {
getSponsorDashboard: vi.fn(),
};
(SponsorService as any).mockImplementation(function() {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const sponsorId = 'sponsor-123';
const apiDto = { id: sponsorId, name: 'Test Sponsor' };
const viewData = { id: sponsorId, name: 'Test Sponsor' };
mockServiceInstance.getSponsorDashboard.mockResolvedValue(Result.ok(apiDto));
(SponsorDashboardViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(sponsorId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SponsorService).toHaveBeenCalled();
expect(mockServiceInstance.getSponsorDashboard).toHaveBeenCalledWith(sponsorId);
expect(SponsorDashboardViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const sponsorId = 'sponsor-123';
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getSponsorDashboard.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(sponsorId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const sponsorId = 'sponsor-123';
const apiDto = { id: sponsorId, name: 'Test Sponsor' };
const viewData = { id: sponsorId, name: 'Test Sponsor' };
mockServiceInstance.getSponsorDashboard.mockResolvedValue(Result.ok(apiDto));
(SponsorDashboardViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await SponsorDashboardPageQuery.execute(sponsorId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,30 @@
import { describe, it, expect } from 'vitest';
import type { SponsorshipRequestsPageDto } from './SponsorshipRequestsPageDto';
describe('SponsorshipRequestsPageDto', () => {
it('should be a types-only file', () => {
// This is a minimal compile-time test to ensure the interface is valid
const dto: SponsorshipRequestsPageDto = {
sections: [
{
entityType: 'driver',
entityId: 'driver-1',
entityName: 'John Doe',
requests: [
{
requestId: 'req-1',
sponsorId: 'sponsor-1',
sponsorName: 'Sponsor A',
message: 'Hello',
createdAtIso: '2024-01-01T00:00:00Z',
raw: {},
},
],
},
],
};
expect(dto.sections).toHaveLength(1);
expect(dto.sections[0].requests).toHaveLength(1);
});
});

View File

@@ -0,0 +1,123 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SponsorshipRequestsPageQuery } from './SponsorshipRequestsPageQuery';
import { SessionGateway } from '@/lib/gateways/SessionGateway';
import { SponsorshipRequestsService } from '@/lib/services/sponsors/SponsorshipRequestsService';
import { SponsorshipRequestsPageViewDataBuilder } from '@/lib/builders/view-data/SponsorshipRequestsPageViewDataBuilder';
import { Result } from '@/lib/contracts/Result';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/gateways/SessionGateway', () => ({
SessionGateway: vi.fn(),
}));
vi.mock('@/lib/services/sponsors/SponsorshipRequestsService', () => ({
SponsorshipRequestsService: vi.fn(),
}));
vi.mock('@/lib/builders/view-data/SponsorshipRequestsPageViewDataBuilder', () => ({
SponsorshipRequestsPageViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('SponsorshipRequestsPageQuery', () => {
let query: SponsorshipRequestsPageQuery;
let mockSessionGatewayInstance: any;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new SponsorshipRequestsPageQuery();
mockSessionGatewayInstance = {
getSession: vi.fn(),
};
(SessionGateway as any).mockImplementation(function() {
return mockSessionGatewayInstance;
});
mockServiceInstance = {
getPendingRequests: vi.fn(),
};
(SponsorshipRequestsService as any).mockImplementation(function() {
return mockServiceInstance;
});
});
it('should return view data when session and service succeed', async () => {
const primaryDriverId = 'driver-123';
const session = { user: { primaryDriverId } };
const apiDto = { sections: [] };
const viewData = { sections: [] };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getPendingRequests.mockResolvedValue(Result.ok(apiDto));
(SponsorshipRequestsPageViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SessionGateway).toHaveBeenCalled();
expect(mockServiceInstance.getPendingRequests).toHaveBeenCalledWith({
entityType: 'driver',
entityId: primaryDriverId,
});
expect(SponsorshipRequestsPageViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return notFound error when session has no primaryDriverId', async () => {
const session = { user: {} };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return notFound error when session is null', async () => {
mockSessionGatewayInstance.getSession.mockResolvedValue(null);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('notFound');
});
it('should return mapped presentation error when service fails', async () => {
const session = { user: { primaryDriverId: 'driver-123' } };
const serviceError = { type: 'serverError' };
const presentationError = 'serverError';
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getPendingRequests.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const session = { user: { primaryDriverId: 'driver-123' } };
const apiDto = { sections: [] };
const viewData = { sections: [] };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getPendingRequests.mockResolvedValue(Result.ok(apiDto));
(SponsorshipRequestsPageViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await SponsorshipRequestsPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,154 @@
/* eslint-disable gridpilot-rules/page-query-filename, gridpilot-rules/single-export-per-file, @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TeamDetailPageQuery } from './TeamDetailPageQuery';
import { TeamService } from '@/lib/services/teams/TeamService';
import { Result } from '@/lib/contracts/Result';
import { TeamDetailViewDataBuilder } from '@/lib/builders/view-data/TeamDetailViewDataBuilder';
import { SessionGateway } from '@/lib/gateways/SessionGateway';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/teams/TeamService', () => ({
TeamService: vi.fn().mockImplementation(function (this: any) {
this.getTeamDetails = vi.fn();
this.getTeamMembers = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/TeamDetailViewDataBuilder', () => ({
TeamDetailViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/gateways/SessionGateway', () => ({
SessionGateway: vi.fn().mockImplementation(function (this: any) {
this.getSession = vi.fn();
}),
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('TeamDetailPageQuery', () => {
let query: TeamDetailPageQuery;
let mockServiceInstance: any;
let mockSessionGatewayInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new TeamDetailPageQuery();
mockServiceInstance = {
getTeamDetails: vi.fn(),
getTeamMembers: vi.fn(),
};
mockSessionGatewayInstance = {
getSession: vi.fn(),
};
(TeamService as any).mockImplementation(function () {
return mockServiceInstance;
});
(SessionGateway as any).mockImplementation(function () {
return mockSessionGatewayInstance;
});
});
it('should return view data when service succeeds', async () => {
const teamId = 'team-123';
const session = { user: { primaryDriverId: 'driver-456' } };
const teamData = { team: { id: teamId, name: 'Test Team', ownerId: 'driver-789' }, membership: null, canManage: false };
const membersData = [{ driverId: 'driver-789', driverName: 'Owner', role: 'owner', joinedAt: '2024-01-01', isActive: true, avatarUrl: 'avatar-url' }];
const viewData = { team: { id: teamId, name: 'Test Team' }, memberships: [], currentDriverId: 'driver-456' };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getTeamDetails.mockResolvedValue(Result.ok(teamData));
mockServiceInstance.getTeamMembers.mockResolvedValue(Result.ok(membersData));
(TeamDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(teamId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SessionGateway).toHaveBeenCalled();
expect(mockSessionGatewayInstance.getSession).toHaveBeenCalled();
expect(TeamService).toHaveBeenCalled();
expect(mockServiceInstance.getTeamDetails).toHaveBeenCalledWith(teamId, 'driver-456');
expect(mockServiceInstance.getTeamMembers).toHaveBeenCalledWith(teamId, 'driver-456', 'driver-789');
expect(TeamDetailViewDataBuilder.build).toHaveBeenCalled();
});
it('should return view data when session has no primaryDriverId', async () => {
const teamId = 'team-123';
const session = { user: null };
const teamData = { team: { id: teamId, name: 'Test Team', ownerId: 'driver-789' }, membership: null, canManage: false };
const membersData = [{ driverId: 'driver-789', driverName: 'Owner', role: 'owner', joinedAt: '2024-01-01', isActive: true, avatarUrl: 'avatar-url' }];
const viewData = { team: { id: teamId, name: 'Test Team' }, memberships: [], currentDriverId: '' };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getTeamDetails.mockResolvedValue(Result.ok(teamData));
mockServiceInstance.getTeamMembers.mockResolvedValue(Result.ok(membersData));
(TeamDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(teamId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(mockServiceInstance.getTeamDetails).toHaveBeenCalledWith(teamId, '');
expect(mockServiceInstance.getTeamMembers).toHaveBeenCalledWith(teamId, '', 'driver-789');
});
it('should return mapped presentation error when team details fail', async () => {
const teamId = 'team-123';
const session = { user: { primaryDriverId: 'driver-456' } };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getTeamDetails.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(teamId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return mapped presentation error when team members fail', async () => {
const teamId = 'team-123';
const session = { user: { primaryDriverId: 'driver-456' } };
const teamData = { team: { id: teamId, name: 'Test Team', ownerId: 'driver-789' }, membership: null, canManage: false };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getTeamDetails.mockResolvedValue(Result.ok(teamData));
mockServiceInstance.getTeamMembers.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(teamId);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const teamId = 'team-123';
const session = { user: { primaryDriverId: 'driver-456' } };
const teamData = { team: { id: teamId, name: 'Test Team', ownerId: 'driver-789' }, membership: null, canManage: false };
const membersData = [{ driverId: 'driver-789', driverName: 'Owner', role: 'owner', joinedAt: '2024-01-01', isActive: true, avatarUrl: 'avatar-url' }];
const viewData = { team: { id: teamId, name: 'Test Team' }, memberships: [], currentDriverId: 'driver-456' };
mockSessionGatewayInstance.getSession.mockResolvedValue(session);
mockServiceInstance.getTeamDetails.mockResolvedValue(Result.ok(teamData));
mockServiceInstance.getTeamMembers.mockResolvedValue(Result.ok(membersData));
(TeamDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await TeamDetailPageQuery.execute(teamId);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,79 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TeamLeaderboardPageQuery } from './TeamLeaderboardPageQuery';
import { TeamService } from '@/lib/services/teams/TeamService';
import { Result } from '@/lib/contracts/Result';
import { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
const mockGetAllTeams = vi.fn();
vi.mock('@/lib/services/teams/TeamService', () => {
return {
TeamService: class {
getAllTeams = mockGetAllTeams;
},
};
});
vi.mock('@/lib/view-models/TeamSummaryViewModel', () => {
const MockVm = vi.fn().mockImplementation(function(data) {
return {
...data,
equals: vi.fn(),
};
});
return { TeamSummaryViewModel: MockVm };
});
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('TeamLeaderboardPageQuery', () => {
let query: TeamLeaderboardPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
getAllTeams: mockGetAllTeams,
};
query = new TeamLeaderboardPageQuery(mockServiceInstance);
});
it('should return view data when service succeeds', async () => {
const apiDto = [{ id: 'team-1', name: 'Test Team' }];
mockServiceInstance.getAllTeams.mockResolvedValue(Result.ok(apiDto));
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap().teams[0]).toMatchObject({ id: 'team-1', name: 'Test Team' });
expect(mockServiceInstance.getAllTeams).toHaveBeenCalled();
expect(TeamSummaryViewModel).toHaveBeenCalledWith({ id: 'team-1', name: 'Test Team' });
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getAllTeams.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return unknown error on exception', async () => {
mockServiceInstance.getAllTeams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('unknown');
});
});

View File

@@ -9,16 +9,27 @@ export interface TeamLeaderboardPageData {
}
export class TeamLeaderboardPageQuery implements PageQuery<TeamLeaderboardPageData, void> {
private readonly service: TeamService;
constructor(service?: TeamService) {
this.service = service || new TeamService();
}
async execute(): Promise<Result<TeamLeaderboardPageData, PresentationError>> {
try {
const service = new TeamService();
const service = this.service;
const result = await service.getAllTeams();
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
const teams = result.unwrap().map((t: any) => new TeamSummaryViewModel(t));
const teams = result.unwrap().map((t: any) => {
const vm = new TeamSummaryViewModel(t as any);
// Ensure it's a plain object for comparison in tests if needed,
// but here we just need it to match the expected viewData structure.
return vm;
});
return Result.ok({ teams });
} catch (error) {

View File

@@ -0,0 +1,83 @@
/* eslint-disable gridpilot-rules/page-query-filename, gridpilot-rules/single-export-per-file, @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TeamRankingsPageQuery } from './TeamRankingsPageQuery';
import { TeamRankingsService } from '@/lib/services/leaderboards/TeamRankingsService';
import { Result } from '@/lib/contracts/Result';
import { TeamRankingsViewDataBuilder } from '@/lib/builders/view-data/TeamRankingsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/leaderboards/TeamRankingsService', () => ({
TeamRankingsService: vi.fn().mockImplementation(function (this: any) {
this.getTeamRankings = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/TeamRankingsViewDataBuilder', () => ({
TeamRankingsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('TeamRankingsPageQuery', () => {
let query: TeamRankingsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new TeamRankingsPageQuery();
mockServiceInstance = {
getTeamRankings: vi.fn(),
};
(TeamRankingsService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { teams: [{ id: 'team-1', name: 'Test Team', points: 100 }] };
const viewData = { teams: [{ id: 'team-1', name: 'Test Team', points: 100 }] };
mockServiceInstance.getTeamRankings.mockResolvedValue(Result.ok(apiDto));
(TeamRankingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(TeamRankingsService).toHaveBeenCalled();
expect(mockServiceInstance.getTeamRankings).toHaveBeenCalled();
expect(TeamRankingsViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getTeamRankings.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should provide a static execute method', async () => {
const apiDto = { teams: [{ id: 'team-1', name: 'Test Team', points: 100 }] };
const viewData = { teams: [{ id: 'team-1', name: 'Test Team', points: 100 }] };
mockServiceInstance.getTeamRankings.mockResolvedValue(Result.ok(apiDto));
(TeamRankingsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await TeamRankingsPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,79 @@
/* eslint-disable gridpilot-rules/page-query-filename, gridpilot-rules/single-export-per-file, @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { TeamsPageQuery } from './TeamsPageQuery';
import { TeamService } from '@/lib/services/teams/TeamService';
import { Result } from '@/lib/contracts/Result';
import { TeamsViewDataBuilder } from '@/lib/builders/view-data/TeamsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/teams/TeamService', () => ({
TeamService: vi.fn().mockImplementation(function (this: any) {
this.getAllTeams = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/TeamsViewDataBuilder', () => ({
TeamsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('TeamsPageQuery', () => {
let query: TeamsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new TeamsPageQuery();
mockServiceInstance = {
getAllTeams: vi.fn(),
};
(TeamService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { teams: [{ id: 'team-1', name: 'Test Team' }] };
const viewData = { teams: [{ id: 'team-1', name: 'Test Team' }] };
mockServiceInstance.getAllTeams.mockResolvedValue(Result.ok(apiDto.teams));
(TeamsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(TeamService).toHaveBeenCalled();
expect(mockServiceInstance.getAllTeams).toHaveBeenCalled();
expect(TeamsViewDataBuilder.build).toHaveBeenCalledWith({ teams: apiDto.teams });
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getAllTeams.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return unknown error on exception', async () => {
mockServiceInstance.getAllTeams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('unknown');
});
});

View File

@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ForgotPasswordPageQuery } from './ForgotPasswordPageQuery';
import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { Result } from '@/lib/contracts/Result';
import { ForgotPasswordViewDataBuilder } from '@/lib/builders/view-data/ForgotPasswordViewDataBuilder';
import { SearchParamParser } from '@/lib/routing/search-params/SearchParamParser';
// Mock dependencies
const mockProcessForgotPasswordParams = vi.fn();
const mockProcessLoginParams = vi.fn();
const mockProcessResetPasswordParams = vi.fn();
const mockProcessSignupParams = vi.fn();
vi.mock('@/lib/services/auth/AuthPageService', () => {
return {
AuthPageService: class {
processForgotPasswordParams = mockProcessForgotPasswordParams;
processLoginParams = mockProcessLoginParams;
processResetPasswordParams = mockProcessResetPasswordParams;
processSignupParams = mockProcessSignupParams;
},
};
});
vi.mock('@/lib/routing/search-params/SearchParamParser', () => ({
SearchParamParser: {
parseAuth: vi.fn(),
},
}));
vi.mock('@/lib/builders/view-data/ForgotPasswordViewDataBuilder', () => ({
ForgotPasswordViewDataBuilder: {
build: vi.fn(),
},
}));
describe('ForgotPasswordPageQuery', () => {
let query: ForgotPasswordPageQuery;
let mockServiceInstance: any;
let mockSearchParams: URLSearchParams;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
processForgotPasswordParams: mockProcessForgotPasswordParams,
};
query = new ForgotPasswordPageQuery(mockServiceInstance as any);
mockSearchParams = new URLSearchParams('returnTo=/login&token=xyz789');
});
it('should return view data when search params are valid and service succeeds', async () => {
const parsedParams = { returnTo: '/login', token: 'xyz789' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processForgotPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ForgotPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(mockSearchParams);
expect(mockServiceInstance.processForgotPasswordParams).toHaveBeenCalledWith(parsedParams);
expect(ForgotPasswordViewDataBuilder.build).toHaveBeenCalledWith(serviceOutput);
});
it('should return error when search params are invalid', async () => {
(SearchParamParser.parseAuth as any).mockReturnValue(Result.err('Invalid params'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Invalid search parameters: Invalid params');
});
it('should return error when service fails', async () => {
const parsedParams = { returnTo: '/login', token: 'xyz789' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processForgotPasswordParams.mockResolvedValue(
Result.err({ message: 'Service error' })
);
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Service error');
});
it('should return error on exception', async () => {
const parsedParams = { returnTo: '/login', token: 'xyz789' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processForgotPasswordParams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Unexpected error');
});
it('should provide a static execute method', async () => {
const parsedParams = { returnTo: '/login', token: 'xyz789' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processForgotPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ForgotPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await ForgotPasswordPageQuery.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
it('should handle Record<string, string | string[] | undefined> input', async () => {
const recordParams = { returnTo: '/login', token: 'xyz789' };
const parsedParams = { returnTo: '/login', token: 'xyz789' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processForgotPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ForgotPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(recordParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(recordParams);
});
});

View File

@@ -6,6 +6,12 @@ import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { ForgotPasswordViewData } from '@/lib/view-data/ForgotPasswordViewData';
export class ForgotPasswordPageQuery implements PageQuery<ForgotPasswordViewData, URLSearchParams | Record<string, string | string[] | undefined>> {
private readonly authService: AuthPageService;
constructor(authService?: AuthPageService) {
this.authService = authService || new AuthPageService();
}
async execute(searchParams: URLSearchParams | Record<string, string | string[] | undefined>): Promise<Result<ForgotPasswordViewData, string>> {
// Parse and validate search parameters
const parsedResult = SearchParamParser.parseAuth(searchParams);
@@ -17,7 +23,7 @@ export class ForgotPasswordPageQuery implements PageQuery<ForgotPasswordViewData
try {
// Use service to process parameters
const authService = new AuthPageService();
const authService = this.authService;
const serviceResult = await authService.processForgotPasswordParams({ returnTo, token });
if (serviceResult.isErr()) {
@@ -27,8 +33,9 @@ export class ForgotPasswordPageQuery implements PageQuery<ForgotPasswordViewData
// Transform to ViewData using builder
const viewData = ForgotPasswordViewDataBuilder.build(serviceResult.unwrap());
return Result.ok(viewData);
} catch (error: any) {
return Result.err(error.message || 'Failed to execute forgot password page query');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute forgot password page query';
return Result.err(message);
}
}

View File

@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { LoginPageQuery } from './LoginPageQuery';
import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { Result } from '@/lib/contracts/Result';
import { LoginViewDataBuilder } from '@/lib/builders/view-data/LoginViewDataBuilder';
import { SearchParamParser } from '@/lib/routing/search-params/SearchParamParser';
// Mock dependencies
const mockProcessForgotPasswordParams = vi.fn();
const mockProcessLoginParams = vi.fn();
const mockProcessResetPasswordParams = vi.fn();
const mockProcessSignupParams = vi.fn();
vi.mock('@/lib/services/auth/AuthPageService', () => {
return {
AuthPageService: class {
processForgotPasswordParams = mockProcessForgotPasswordParams;
processLoginParams = mockProcessLoginParams;
processResetPasswordParams = mockProcessResetPasswordParams;
processSignupParams = mockProcessSignupParams;
},
};
});
vi.mock('@/lib/routing/search-params/SearchParamParser', () => ({
SearchParamParser: {
parseAuth: vi.fn(),
},
}));
vi.mock('@/lib/builders/view-data/LoginViewDataBuilder', () => ({
LoginViewDataBuilder: {
build: vi.fn(),
},
}));
describe('LoginPageQuery', () => {
let query: LoginPageQuery;
let mockServiceInstance: any;
let mockSearchParams: URLSearchParams;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
processLoginParams: mockProcessLoginParams,
};
query = new LoginPageQuery(mockServiceInstance as any);
mockSearchParams = new URLSearchParams('returnTo=/dashboard&token=abc123');
});
it('should return view data when search params are valid and service succeeds', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'abc123' };
const serviceOutput = { success: true };
const viewData = { returnTo: '/dashboard', token: 'abc123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processLoginParams.mockResolvedValue(Result.ok(serviceOutput));
(LoginViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(mockSearchParams);
expect(mockServiceInstance.processLoginParams).toHaveBeenCalledWith(parsedParams);
expect(LoginViewDataBuilder.build).toHaveBeenCalledWith(serviceOutput);
});
it('should return error when search params are invalid', async () => {
(SearchParamParser.parseAuth as any).mockReturnValue(Result.err('Invalid params'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Invalid search parameters: Invalid params');
});
it('should return error when service fails', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'abc123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processLoginParams.mockResolvedValue(
Result.err({ message: 'Service error' })
);
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Service error');
});
it('should return error on exception', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'abc123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processLoginParams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Unexpected error');
});
it('should provide a static execute method', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'abc123' };
const serviceOutput = { success: true };
const viewData = { returnTo: '/dashboard', token: 'abc123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processLoginParams.mockResolvedValue(Result.ok(serviceOutput));
(LoginViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await LoginPageQuery.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
it('should handle Record<string, string | string[] | undefined> input', async () => {
const recordParams = { returnTo: '/dashboard', token: 'abc123' };
const parsedParams = { returnTo: '/dashboard', token: 'abc123' };
const serviceOutput = { success: true };
const viewData = { returnTo: '/dashboard', token: 'abc123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processLoginParams.mockResolvedValue(Result.ok(serviceOutput));
(LoginViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(recordParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(recordParams);
});
});

View File

@@ -6,6 +6,12 @@ import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { LoginViewData } from '@/lib/view-data/LoginViewData';
export class LoginPageQuery implements PageQuery<LoginViewData, URLSearchParams | Record<string, string | string[] | undefined>> {
private readonly authService: AuthPageService;
constructor(authService?: AuthPageService) {
this.authService = authService || new AuthPageService();
}
async execute(searchParams: URLSearchParams | Record<string, string | string[] | undefined>): Promise<Result<LoginViewData, string>> {
// Parse and validate search parameters
const parsedResult = SearchParamParser.parseAuth(searchParams);
@@ -17,7 +23,7 @@ export class LoginPageQuery implements PageQuery<LoginViewData, URLSearchParams
try {
// Use service to process parameters
const authService = new AuthPageService();
const authService = this.authService;
const serviceResult = await authService.processLoginParams({ returnTo, token });
if (serviceResult.isErr()) {
@@ -27,8 +33,9 @@ export class LoginPageQuery implements PageQuery<LoginViewData, URLSearchParams
// Transform to ViewData using builder
const viewData = LoginViewDataBuilder.build(serviceResult.unwrap());
return Result.ok(viewData);
} catch (error: any) {
return Result.err(error.message || 'Failed to execute login page query');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute login page query';
return Result.err(message);
}
}

View File

@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ResetPasswordPageQuery } from './ResetPasswordPageQuery';
import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { Result } from '@/lib/contracts/Result';
import { ResetPasswordViewDataBuilder } from '@/lib/builders/view-data/ResetPasswordViewDataBuilder';
import { SearchParamParser } from '@/lib/routing/search-params/SearchParamParser';
// Mock dependencies
const mockProcessForgotPasswordParams = vi.fn();
const mockProcessLoginParams = vi.fn();
const mockProcessResetPasswordParams = vi.fn();
const mockProcessSignupParams = vi.fn();
vi.mock('@/lib/services/auth/AuthPageService', () => {
return {
AuthPageService: class {
processForgotPasswordParams = mockProcessForgotPasswordParams;
processLoginParams = mockProcessLoginParams;
processResetPasswordParams = mockProcessResetPasswordParams;
processSignupParams = mockProcessSignupParams;
},
};
});
vi.mock('@/lib/routing/search-params/SearchParamParser', () => ({
SearchParamParser: {
parseAuth: vi.fn(),
},
}));
vi.mock('@/lib/builders/view-data/ResetPasswordViewDataBuilder', () => ({
ResetPasswordViewDataBuilder: {
build: vi.fn(),
},
}));
describe('ResetPasswordPageQuery', () => {
let query: ResetPasswordPageQuery;
let mockServiceInstance: any;
let mockSearchParams: URLSearchParams;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
processResetPasswordParams: mockProcessResetPasswordParams,
};
query = new ResetPasswordPageQuery(mockServiceInstance as any);
mockSearchParams = new URLSearchParams('returnTo=/login&token=reset123');
});
it('should return view data when search params are valid and service succeeds', async () => {
const parsedParams = { returnTo: '/login', token: 'reset123' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processResetPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ResetPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(mockSearchParams);
expect(mockServiceInstance.processResetPasswordParams).toHaveBeenCalledWith(parsedParams);
expect(ResetPasswordViewDataBuilder.build).toHaveBeenCalledWith(serviceOutput);
});
it('should return error when search params are invalid', async () => {
(SearchParamParser.parseAuth as any).mockReturnValue(Result.err('Invalid params'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Invalid search parameters: Invalid params');
});
it('should return error when service fails', async () => {
const parsedParams = { returnTo: '/login', token: 'reset123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processResetPasswordParams.mockResolvedValue(
Result.err({ message: 'Service error' })
);
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Service error');
});
it('should return error on exception', async () => {
const parsedParams = { returnTo: '/login', token: 'reset123' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processResetPasswordParams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Unexpected error');
});
it('should provide a static execute method', async () => {
const parsedParams = { returnTo: '/login', token: 'reset123' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processResetPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ResetPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await ResetPasswordPageQuery.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
it('should handle Record<string, string | string[] | undefined> input', async () => {
const recordParams = { returnTo: '/login', token: 'reset123' };
const parsedParams = { returnTo: '/login', token: 'reset123' };
const serviceOutput = { email: 'test@example.com' };
const viewData = { email: 'test@example.com', returnTo: '/login' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processResetPasswordParams.mockResolvedValue(Result.ok(serviceOutput));
(ResetPasswordViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(recordParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(recordParams);
});
});

View File

@@ -6,6 +6,12 @@ import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { ResetPasswordViewData } from '@/lib/view-data/ResetPasswordViewData';
export class ResetPasswordPageQuery implements PageQuery<ResetPasswordViewData, URLSearchParams | Record<string, string | string[] | undefined>> {
private readonly authService: AuthPageService;
constructor(authService?: AuthPageService) {
this.authService = authService || new AuthPageService();
}
async execute(searchParams: URLSearchParams | Record<string, string | string[] | undefined>): Promise<Result<ResetPasswordViewData, string>> {
// Parse and validate search parameters
const parsedResult = SearchParamParser.parseAuth(searchParams);
@@ -17,7 +23,7 @@ export class ResetPasswordPageQuery implements PageQuery<ResetPasswordViewData,
try {
// Use service to process parameters
const authService = new AuthPageService();
const authService = this.authService;
const serviceResult = await authService.processResetPasswordParams({ returnTo, token });
if (serviceResult.isErr()) {
@@ -27,8 +33,9 @@ export class ResetPasswordPageQuery implements PageQuery<ResetPasswordViewData,
// Transform to ViewData using builder
const viewData = ResetPasswordViewDataBuilder.build(serviceResult.unwrap());
return Result.ok(viewData);
} catch (error: any) {
return Result.err(error.message || 'Failed to execute reset password page query');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute reset password page query';
return Result.err(message);
}
}

View File

@@ -0,0 +1,134 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SignupPageQuery } from './SignupPageQuery';
import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { Result } from '@/lib/contracts/Result';
import { SignupViewDataBuilder } from '@/lib/builders/view-data/SignupViewDataBuilder';
import { SearchParamParser } from '@/lib/routing/search-params/SearchParamParser';
// Mock dependencies
const mockProcessForgotPasswordParams = vi.fn();
const mockProcessLoginParams = vi.fn();
const mockProcessResetPasswordParams = vi.fn();
const mockProcessSignupParams = vi.fn();
vi.mock('@/lib/services/auth/AuthPageService', () => {
return {
AuthPageService: class {
processForgotPasswordParams = mockProcessForgotPasswordParams;
processLoginParams = mockProcessLoginParams;
processResetPasswordParams = mockProcessResetPasswordParams;
processSignupParams = mockProcessSignupParams;
},
};
});
vi.mock('@/lib/routing/search-params/SearchParamParser', () => ({
SearchParamParser: {
parseAuth: vi.fn(),
},
}));
vi.mock('@/lib/builders/view-data/SignupViewDataBuilder', () => ({
SignupViewDataBuilder: {
build: vi.fn(),
},
}));
describe('SignupPageQuery', () => {
let query: SignupPageQuery;
let mockServiceInstance: any;
let mockSearchParams: URLSearchParams;
beforeEach(() => {
vi.clearAllMocks();
mockServiceInstance = {
processSignupParams: mockProcessSignupParams,
};
query = new SignupPageQuery(mockServiceInstance as any);
mockSearchParams = new URLSearchParams('returnTo=/dashboard&token=signup456');
});
it('should return view data when search params are valid and service succeeds', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'signup456' };
const serviceOutput = { email: 'newuser@example.com' };
const viewData = { email: 'newuser@example.com', returnTo: '/dashboard' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processSignupParams.mockResolvedValue(Result.ok(serviceOutput));
(SignupViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(mockSearchParams);
expect(mockServiceInstance.processSignupParams).toHaveBeenCalledWith(parsedParams);
expect(SignupViewDataBuilder.build).toHaveBeenCalledWith(serviceOutput);
});
it('should return error when search params are invalid', async () => {
(SearchParamParser.parseAuth as any).mockReturnValue(Result.err('Invalid params'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Invalid search parameters: Invalid params');
});
it('should return error when service fails', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'signup456' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processSignupParams.mockResolvedValue(
Result.err({ message: 'Service error' })
);
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Service error');
});
it('should return error on exception', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'signup456' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processSignupParams.mockRejectedValue(new Error('Unexpected error'));
const result = await query.execute(mockSearchParams);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Unexpected error');
});
it('should provide a static execute method', async () => {
const parsedParams = { returnTo: '/dashboard', token: 'signup456' };
const serviceOutput = { email: 'newuser@example.com' };
const viewData = { email: 'newuser@example.com', returnTo: '/dashboard' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processSignupParams.mockResolvedValue(Result.ok(serviceOutput));
(SignupViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await SignupPageQuery.execute(mockSearchParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
it('should handle Record<string, string | string[] | undefined> input', async () => {
const recordParams = { returnTo: '/dashboard', token: 'signup456' };
const parsedParams = { returnTo: '/dashboard', token: 'signup456' };
const serviceOutput = { email: 'newuser@example.com' };
const viewData = { email: 'newuser@example.com', returnTo: '/dashboard' };
(SearchParamParser.parseAuth as any).mockReturnValue(Result.ok(parsedParams));
mockServiceInstance.processSignupParams.mockResolvedValue(Result.ok(serviceOutput));
(SignupViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(recordParams);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(SearchParamParser.parseAuth).toHaveBeenCalledWith(recordParams);
});
});

View File

@@ -6,6 +6,12 @@ import { AuthPageService } from '@/lib/services/auth/AuthPageService';
import { SignupViewData } from '@/lib/view-data/SignupViewData';
export class SignupPageQuery implements PageQuery<SignupViewData, URLSearchParams | Record<string, string | string[] | undefined>> {
private readonly authService: AuthPageService;
constructor(authService?: AuthPageService) {
this.authService = authService || new AuthPageService();
}
async execute(searchParams: URLSearchParams | Record<string, string | string[] | undefined>): Promise<Result<SignupViewData, string>> {
// Parse and validate search parameters
const parsedResult = SearchParamParser.parseAuth(searchParams);
@@ -17,7 +23,7 @@ export class SignupPageQuery implements PageQuery<SignupViewData, URLSearchParam
try {
// Use service to process parameters
const authService = new AuthPageService();
const authService = this.authService;
const serviceResult = await authService.processSignupParams({ returnTo, token });
if (serviceResult.isErr()) {
@@ -27,8 +33,9 @@ export class SignupPageQuery implements PageQuery<SignupViewData, URLSearchParam
// Transform to ViewData using builder
const viewData = SignupViewDataBuilder.build(serviceResult.unwrap());
return Result.ok(viewData);
} catch (error: any) {
return Result.err(error.message || 'Failed to execute signup page query');
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute signup page query';
return Result.err(message);
}
}

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetAvatarPageQuery } from './GetAvatarPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { AvatarViewDataBuilder } from '@/lib/builders/view-data/AvatarViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getAvatar = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/AvatarViewDataBuilder', () => ({
AvatarViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetAvatarPageQuery', () => {
let query: GetAvatarPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetAvatarPageQuery();
mockServiceInstance = {
getAvatar: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { driverId: 'driver-123' };
const apiDto = { url: 'avatar-url', data: 'base64-data' };
const viewData = { url: 'avatar-url', data: 'base64-data' };
mockServiceInstance.getAvatar.mockResolvedValue(Result.ok(apiDto));
(AvatarViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getAvatar).toHaveBeenCalledWith('driver-123');
expect(AvatarViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { driverId: 'driver-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getAvatar.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { driverId: 'driver-123' };
mockServiceInstance.getAvatar.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { driverId: 'driver-123' };
const apiDto = { url: 'avatar-url', data: 'base64-data' };
const viewData = { url: 'avatar-url', data: 'base64-data' };
mockServiceInstance.getAvatar.mockResolvedValue(Result.ok(apiDto));
(AvatarViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetAvatarPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetCategoryIconPageQuery } from './GetCategoryIconPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { CategoryIconViewDataBuilder } from '@/lib/builders/view-data/CategoryIconViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getCategoryIcon = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/CategoryIconViewDataBuilder', () => ({
CategoryIconViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetCategoryIconPageQuery', () => {
let query: GetCategoryIconPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetCategoryIconPageQuery();
mockServiceInstance = {
getCategoryIcon: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { categoryId: 'category-123' };
const apiDto = { url: 'icon-url', data: 'base64-data' };
const viewData = { url: 'icon-url', data: 'base64-data' };
mockServiceInstance.getCategoryIcon.mockResolvedValue(Result.ok(apiDto));
(CategoryIconViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getCategoryIcon).toHaveBeenCalledWith('category-123');
expect(CategoryIconViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { categoryId: 'category-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getCategoryIcon.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { categoryId: 'category-123' };
mockServiceInstance.getCategoryIcon.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { categoryId: 'category-123' };
const apiDto = { url: 'icon-url', data: 'base64-data' };
const viewData = { url: 'icon-url', data: 'base64-data' };
mockServiceInstance.getCategoryIcon.mockResolvedValue(Result.ok(apiDto));
(CategoryIconViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetCategoryIconPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetLeagueCoverPageQuery } from './GetLeagueCoverPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { LeagueCoverViewDataBuilder } from '@/lib/builders/view-data/LeagueCoverViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getLeagueCover = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueCoverViewDataBuilder', () => ({
LeagueCoverViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetLeagueCoverPageQuery', () => {
let query: GetLeagueCoverPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetLeagueCoverPageQuery();
mockServiceInstance = {
getLeagueCover: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { leagueId: 'league-123' };
const apiDto = { url: 'cover-url', data: 'base64-data' };
const viewData = { url: 'cover-url', data: 'base64-data' };
mockServiceInstance.getLeagueCover.mockResolvedValue(Result.ok(apiDto));
(LeagueCoverViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getLeagueCover).toHaveBeenCalledWith('league-123');
expect(LeagueCoverViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { leagueId: 'league-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getLeagueCover.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { leagueId: 'league-123' };
mockServiceInstance.getLeagueCover.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { leagueId: 'league-123' };
const apiDto = { url: 'cover-url', data: 'base64-data' };
const viewData = { url: 'cover-url', data: 'base64-data' };
mockServiceInstance.getLeagueCover.mockResolvedValue(Result.ok(apiDto));
(LeagueCoverViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetLeagueCoverPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetLeagueLogoPageQuery } from './GetLeagueLogoPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { LeagueLogoViewDataBuilder } from '@/lib/builders/view-data/LeagueLogoViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getLeagueLogo = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/LeagueLogoViewDataBuilder', () => ({
LeagueLogoViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetLeagueLogoPageQuery', () => {
let query: GetLeagueLogoPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetLeagueLogoPageQuery();
mockServiceInstance = {
getLeagueLogo: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { leagueId: 'league-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getLeagueLogo.mockResolvedValue(Result.ok(apiDto));
(LeagueLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getLeagueLogo).toHaveBeenCalledWith('league-123');
expect(LeagueLogoViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { leagueId: 'league-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getLeagueLogo.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { leagueId: 'league-123' };
mockServiceInstance.getLeagueLogo.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { leagueId: 'league-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getLeagueLogo.mockResolvedValue(Result.ok(apiDto));
(LeagueLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetLeagueLogoPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetSponsorLogoPageQuery } from './GetSponsorLogoPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { SponsorLogoViewDataBuilder } from '@/lib/builders/view-data/SponsorLogoViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getSponsorLogo = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/SponsorLogoViewDataBuilder', () => ({
SponsorLogoViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetSponsorLogoPageQuery', () => {
let query: GetSponsorLogoPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetSponsorLogoPageQuery();
mockServiceInstance = {
getSponsorLogo: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { sponsorId: 'sponsor-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getSponsorLogo.mockResolvedValue(Result.ok(apiDto));
(SponsorLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getSponsorLogo).toHaveBeenCalledWith('sponsor-123');
expect(SponsorLogoViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { sponsorId: 'sponsor-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getSponsorLogo.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { sponsorId: 'sponsor-123' };
mockServiceInstance.getSponsorLogo.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { sponsorId: 'sponsor-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getSponsorLogo.mockResolvedValue(Result.ok(apiDto));
(SponsorLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetSponsorLogoPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetTeamLogoPageQuery } from './GetTeamLogoPageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { TeamLogoViewDataBuilder } from '@/lib/builders/view-data/TeamLogoViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getTeamLogo = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/TeamLogoViewDataBuilder', () => ({
TeamLogoViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetTeamLogoPageQuery', () => {
let query: GetTeamLogoPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetTeamLogoPageQuery();
mockServiceInstance = {
getTeamLogo: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { teamId: 'team-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getTeamLogo.mockResolvedValue(Result.ok(apiDto));
(TeamLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getTeamLogo).toHaveBeenCalledWith('team-123');
expect(TeamLogoViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { teamId: 'team-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getTeamLogo.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { teamId: 'team-123' };
mockServiceInstance.getTeamLogo.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { teamId: 'team-123' };
const apiDto = { url: 'logo-url', data: 'base64-data' };
const viewData = { url: 'logo-url', data: 'base64-data' };
mockServiceInstance.getTeamLogo.mockResolvedValue(Result.ok(apiDto));
(TeamLogoViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetTeamLogoPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetTrackImagePageQuery } from './GetTrackImagePageQuery';
import { MediaService } from '@/lib/services/media/MediaService';
import { Result } from '@/lib/contracts/Result';
import { TrackImageViewDataBuilder } from '@/lib/builders/view-data/TrackImageViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/media/MediaService', () => ({
MediaService: vi.fn(class {
getTrackImage = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/TrackImageViewDataBuilder', () => ({
TrackImageViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('GetTrackImagePageQuery', () => {
let query: GetTrackImagePageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new GetTrackImagePageQuery();
mockServiceInstance = {
getTrackImage: vi.fn(),
};
(MediaService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { trackId: 'track-123' };
const apiDto = { url: 'image-url', data: 'base64-data' };
const viewData = { url: 'image-url', data: 'base64-data' };
mockServiceInstance.getTrackImage.mockResolvedValue(Result.ok(apiDto));
(TrackImageViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(MediaService).toHaveBeenCalled();
expect(mockServiceInstance.getTrackImage).toHaveBeenCalledWith('track-123');
expect(TrackImageViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { trackId: 'track-123' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getTrackImage.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { trackId: 'track-123' };
mockServiceInstance.getTrackImage.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('serverError');
});
it('should provide a static execute method', async () => {
const params = { trackId: 'track-123' };
const apiDto = { url: 'image-url', data: 'base64-data' };
const viewData = { url: 'image-url', data: 'base64-data' };
mockServiceInstance.getTrackImage.mockResolvedValue(Result.ok(apiDto));
(TrackImageViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await GetTrackImagePageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -0,0 +1,111 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RaceDetailPageQuery } from './RaceDetailPageQuery';
import { RacesService } from '@/lib/services/races/RacesService';
import { Result } from '@/lib/contracts/Result';
import { RaceDetailViewDataBuilder } from '@/lib/builders/view-data/RaceDetailViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/races/RacesService', () => ({
RacesService: vi.fn(class {
getRaceDetail = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RaceDetailViewDataBuilder', () => ({
RaceDetailViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('RaceDetailPageQuery', () => {
let query: RaceDetailPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new RaceDetailPageQuery();
mockServiceInstance = {
getRaceDetail: vi.fn(),
};
(RacesService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, driver: { id: 'driver-456' } };
const viewData = { race: { id: 'race-123' }, driver: { id: 'driver-456' } };
mockServiceInstance.getRaceDetail.mockResolvedValue(Result.ok(apiDto));
(RaceDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(RacesService).toHaveBeenCalled();
expect(mockServiceInstance.getRaceDetail).toHaveBeenCalledWith('race-123', 'driver-456');
expect(RaceDetailViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return view data when driverId is optional', async () => {
const params = { raceId: 'race-123' };
const apiDto = { race: { id: 'race-123' } };
const viewData = { race: { id: 'race-123' } };
mockServiceInstance.getRaceDetail.mockResolvedValue(Result.ok(apiDto));
(RaceDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(mockServiceInstance.getRaceDetail).toHaveBeenCalledWith('race-123', '');
});
it('should return mapped presentation error when service fails', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRaceDetail.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
mockServiceInstance.getRaceDetail.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Network error');
});
it('should provide a static execute method', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, driver: { id: 'driver-456' } };
const viewData = { race: { id: 'race-123' }, driver: { id: 'driver-456' } };
mockServiceInstance.getRaceDetail.mockResolvedValue(Result.ok(apiDto));
(RaceDetailViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await RaceDetailPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -18,19 +18,24 @@ interface RaceDetailPageQueryParams {
*/
export class RaceDetailPageQuery implements PageQuery<RaceDetailViewData, RaceDetailPageQueryParams> {
async execute(params: RaceDetailPageQueryParams): Promise<Result<RaceDetailViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get race detail data
const result = await service.getRaceDetail(params.raceId, params.driverId || '');
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
try {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get race detail data
const result = await service.getRaceDetail(params.raceId, params.driverId || '');
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
// Transform to ViewData using builder
const viewData = RaceDetailViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute race detail page query';
return Result.err(message as PresentationError);
}
// Transform to ViewData using builder
const viewData = RaceDetailViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
}
// Static method to avoid object construction in server code

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RaceResultsPageQuery } from './RaceResultsPageQuery';
import { RaceResultsService } from '@/lib/services/races/RaceResultsService';
import { Result } from '@/lib/contracts/Result';
import { RaceResultsViewDataBuilder } from '@/lib/builders/view-data/RaceResultsViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/races/RaceResultsService', () => ({
RaceResultsService: vi.fn(class {
getRaceResultsDetail = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RaceResultsViewDataBuilder', () => ({
RaceResultsViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('RaceResultsPageQuery', () => {
let query: RaceResultsPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new RaceResultsPageQuery();
mockServiceInstance = {
getRaceResultsDetail: vi.fn(),
};
(RaceResultsService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, results: [{ position: 1 }] };
const viewData = { race: { id: 'race-123' }, results: [{ position: 1 }] };
mockServiceInstance.getRaceResultsDetail.mockResolvedValue(Result.ok(apiDto));
(RaceResultsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(RaceResultsService).toHaveBeenCalled();
expect(mockServiceInstance.getRaceResultsDetail).toHaveBeenCalledWith('race-123');
expect(RaceResultsViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRaceResultsDetail.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
mockServiceInstance.getRaceResultsDetail.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Network error');
});
it('should provide a static execute method', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, results: [{ position: 1 }] };
const viewData = { race: { id: 'race-123' }, results: [{ position: 1 }] };
mockServiceInstance.getRaceResultsDetail.mockResolvedValue(Result.ok(apiDto));
(RaceResultsViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await RaceResultsPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -18,19 +18,24 @@ interface RaceResultsPageQueryParams {
*/
export class RaceResultsPageQuery implements PageQuery<RaceResultsViewData, RaceResultsPageQueryParams> {
async execute(params: RaceResultsPageQueryParams): Promise<Result<RaceResultsViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new RaceResultsService();
// Get race results data
const result = await service.getRaceResultsDetail(params.raceId);
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
try {
// Manual wiring: Service creates its own dependencies
const service = new RaceResultsService();
// Get race results data
const result = await service.getRaceResultsDetail(params.raceId);
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
// Transform to ViewData using builder
const viewData = RaceResultsViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute race results page query';
return Result.err(message as PresentationError);
}
// Transform to ViewData using builder
const viewData = RaceResultsViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
}
// Static method to avoid object construction in server code

View File

@@ -0,0 +1,96 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RaceStewardingPageQuery } from './RaceStewardingPageQuery';
import { RaceStewardingService } from '@/lib/services/races/RaceStewardingService';
import { Result } from '@/lib/contracts/Result';
import { RaceStewardingViewDataBuilder } from '@/lib/builders/view-data/RaceStewardingViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/races/RaceStewardingService', () => ({
RaceStewardingService: vi.fn(class {
getRaceStewarding = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RaceStewardingViewDataBuilder', () => ({
RaceStewardingViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('RaceStewardingPageQuery', () => {
let query: RaceStewardingPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new RaceStewardingPageQuery();
mockServiceInstance = {
getRaceStewarding: vi.fn(),
};
(RaceStewardingService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, stewarding: [{ incident: 'incident-1' }] };
const viewData = { race: { id: 'race-123' }, stewarding: [{ incident: 'incident-1' }] };
mockServiceInstance.getRaceStewarding.mockResolvedValue(Result.ok(apiDto));
(RaceStewardingViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(RaceStewardingService).toHaveBeenCalled();
expect(mockServiceInstance.getRaceStewarding).toHaveBeenCalledWith('race-123');
expect(RaceStewardingViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRaceStewarding.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
mockServiceInstance.getRaceStewarding.mockRejectedValue(new Error('Network error'));
const result = await query.execute(params);
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Network error');
});
it('should provide a static execute method', async () => {
const params = { raceId: 'race-123', driverId: 'driver-456' };
const apiDto = { race: { id: 'race-123' }, stewarding: [{ incident: 'incident-1' }] };
const viewData = { race: { id: 'race-123' }, stewarding: [{ incident: 'incident-1' }] };
mockServiceInstance.getRaceStewarding.mockResolvedValue(Result.ok(apiDto));
(RaceStewardingViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await RaceStewardingPageQuery.execute(params);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -18,19 +18,24 @@ interface RaceStewardingPageQueryParams {
*/
export class RaceStewardingPageQuery implements PageQuery<RaceStewardingViewData, RaceStewardingPageQueryParams> {
async execute(params: RaceStewardingPageQueryParams): Promise<Result<RaceStewardingViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new RaceStewardingService();
// Get race stewarding data
const result = await service.getRaceStewarding(params.raceId);
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
try {
// Manual wiring: Service creates its own dependencies
const service = new RaceStewardingService();
// Get race stewarding data
const result = await service.getRaceStewarding(params.raceId);
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
// Transform to ViewData using builder
const viewData = RaceStewardingViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute race stewarding page query';
return Result.err(message as PresentationError);
}
// Transform to ViewData using builder
const viewData = RaceStewardingViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
}
// Static method to avoid object construction in server code

View File

@@ -0,0 +1,91 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RacesAllPageQuery } from './RacesAllPageQuery';
import { RacesService } from '@/lib/services/races/RacesService';
import { Result } from '@/lib/contracts/Result';
import { RacesViewDataBuilder } from '@/lib/builders/view-data/RacesViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/races/RacesService', () => ({
RacesService: vi.fn(class {
getAllRacesPageData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RacesViewDataBuilder', () => ({
RacesViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('RacesAllPageQuery', () => {
let query: RacesAllPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new RacesAllPageQuery();
mockServiceInstance = {
getAllRacesPageData: vi.fn(),
};
(RacesService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { races: [{ id: 'race-123' }], categories: [{ id: 'cat-1' }] };
const viewData = { races: [{ id: 'race-123' }], categories: [{ id: 'cat-1' }] };
mockServiceInstance.getAllRacesPageData.mockResolvedValue(Result.ok(apiDto));
(RacesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(RacesService).toHaveBeenCalled();
expect(mockServiceInstance.getAllRacesPageData).toHaveBeenCalled();
expect(RacesViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getAllRacesPageData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
mockServiceInstance.getAllRacesPageData.mockRejectedValue(new Error('Network error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Network error');
});
it('should provide a static execute method', async () => {
const apiDto = { races: [{ id: 'race-123' }], categories: [{ id: 'cat-1' }] };
const viewData = { races: [{ id: 'race-123' }], categories: [{ id: 'cat-1' }] };
mockServiceInstance.getAllRacesPageData.mockResolvedValue(Result.ok(apiDto));
(RacesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await RacesAllPageQuery.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
});
});

View File

@@ -13,19 +13,24 @@ import { RacesViewDataBuilder } from '@/lib/builders/view-data/RacesViewDataBuil
*/
export class RacesAllPageQuery implements PageQuery<RacesViewData, void> {
async execute(): Promise<Result<RacesViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get all races data
const result = await service.getAllRacesPageData();
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
try {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get all races data
const result = await service.getAllRacesPageData();
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
// Transform to ViewData using builder
const viewData = RacesViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute races all page query';
return Result.err(message as PresentationError);
}
// Transform to ViewData using builder
const viewData = RacesViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
}
// Static method to avoid object construction in server code

View File

@@ -0,0 +1,79 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { RacesPageQuery } from './RacesPageQuery';
import { RacesService } from '@/lib/services/races/RacesService';
import { Result } from '@/lib/contracts/Result';
import { RacesViewDataBuilder } from '@/lib/builders/view-data/RacesViewDataBuilder';
import { mapToPresentationError } from '@/lib/contracts/page-queries/PresentationError';
// Mock dependencies
vi.mock('@/lib/services/races/RacesService', () => ({
RacesService: vi.fn(class {
getRacesPageData = vi.fn();
}),
}));
vi.mock('@/lib/builders/view-data/RacesViewDataBuilder', () => ({
RacesViewDataBuilder: {
build: vi.fn(),
},
}));
vi.mock('@/lib/contracts/page-queries/PresentationError', () => ({
mapToPresentationError: vi.fn(),
}));
describe('RacesPageQuery', () => {
let query: RacesPageQuery;
let mockServiceInstance: any;
beforeEach(() => {
vi.clearAllMocks();
query = new RacesPageQuery();
mockServiceInstance = {
getRacesPageData: vi.fn(),
};
(RacesService as any).mockImplementation(function () {
return mockServiceInstance;
});
});
it('should return view data when service succeeds', async () => {
const apiDto = { races: [{ id: 'race-123' }], featured: [{ id: 'race-456' }] };
const viewData = { races: [{ id: 'race-123' }], featured: [{ id: 'race-456' }] };
mockServiceInstance.getRacesPageData.mockResolvedValue(Result.ok(apiDto));
(RacesViewDataBuilder.build as any).mockReturnValue(viewData);
const result = await query.execute();
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toEqual(viewData);
expect(RacesService).toHaveBeenCalled();
expect(mockServiceInstance.getRacesPageData).toHaveBeenCalled();
expect(RacesViewDataBuilder.build).toHaveBeenCalledWith(apiDto);
});
it('should return mapped presentation error when service fails', async () => {
const serviceError = { type: 'notFound' };
const presentationError = 'notFound';
mockServiceInstance.getRacesPageData.mockResolvedValue(Result.err(serviceError));
(mapToPresentationError as any).mockReturnValue(presentationError);
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe(presentationError);
expect(mapToPresentationError).toHaveBeenCalledWith(serviceError);
});
it('should return serverError on exception', async () => {
mockServiceInstance.getRacesPageData.mockRejectedValue(new Error('Network error'));
const result = await query.execute();
expect(result.isErr()).toBe(true);
expect(result.getError()).toBe('Network error');
});
});

View File

@@ -13,18 +13,23 @@ import { RacesViewDataBuilder } from '@/lib/builders/view-data/RacesViewDataBuil
*/
export class RacesPageQuery implements PageQuery<RacesViewData, void> {
async execute(): Promise<Result<RacesViewData, PresentationError>> {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get races data
const result = await service.getRacesPageData();
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
try {
// Manual wiring: Service creates its own dependencies
const service = new RacesService();
// Get races data
const result = await service.getRacesPageData();
if (result.isErr()) {
return Result.err(mapToPresentationError(result.getError()));
}
// Transform to ViewData using builder
const viewData = RacesViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
} catch (error: unknown) {
const message = error instanceof Error ? error.message : 'Failed to execute races page query';
return Result.err(message as PresentationError);
}
// Transform to ViewData using builder
const viewData = RacesViewDataBuilder.build(result.unwrap());
return Result.ok(viewData);
}
}