742 lines
22 KiB
TypeScript
742 lines
22 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { DashboardService } from '@/lib/services/analytics/DashboardService';
|
|
import { DashboardApiClient } from '@/lib/api/dashboard/DashboardApiClient';
|
|
import { AnalyticsApiClient } from '@/lib/api/analytics/AnalyticsApiClient';
|
|
import { ApiError } from '@/lib/api/base/ApiError';
|
|
import type { DashboardOverviewDTO } from '@/lib/types/generated/DashboardOverviewDTO';
|
|
import type { GetAnalyticsMetricsOutputDTO } from '@/lib/types/generated/GetAnalyticsMetricsOutputDTO';
|
|
|
|
// Mock the API clients
|
|
vi.mock('@/lib/api/dashboard/DashboardApiClient');
|
|
vi.mock('@/lib/api/analytics/AnalyticsApiClient');
|
|
|
|
describe('DashboardService', () => {
|
|
let service: DashboardService;
|
|
let mockDashboardApiClient: any;
|
|
let mockAnalyticsApiClient: any;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
service = new DashboardService();
|
|
mockDashboardApiClient = (service as any).apiClient;
|
|
mockAnalyticsApiClient = (service as any).analyticsApiClient;
|
|
});
|
|
|
|
describe('happy paths', () => {
|
|
it('should successfully fetch dashboard overview', async () => {
|
|
const mockOverview: DashboardOverviewDTO = {
|
|
currentDriver: {
|
|
id: 'driver-123',
|
|
name: 'Test Driver',
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
rating: 1500,
|
|
rank: 10,
|
|
},
|
|
myUpcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
],
|
|
otherUpcomingRaces: [
|
|
{
|
|
id: 'race-2',
|
|
name: 'Race 2',
|
|
date: '2024-01-16T10:00:00.000Z',
|
|
track: 'Track 2',
|
|
league: 'League 2',
|
|
},
|
|
],
|
|
upcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
{
|
|
id: 'race-2',
|
|
name: 'Race 2',
|
|
date: '2024-01-16T10:00:00.000Z',
|
|
track: 'Track 2',
|
|
league: 'League 2',
|
|
},
|
|
],
|
|
activeLeaguesCount: 3,
|
|
nextRace: {
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
recentResults: [
|
|
{
|
|
raceId: 'race-0',
|
|
position: 5,
|
|
points: 15,
|
|
date: '2024-01-10T10:00:00.000Z',
|
|
},
|
|
],
|
|
leagueStandingsSummaries: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
position: 3,
|
|
points: 150,
|
|
},
|
|
],
|
|
feedSummary: {
|
|
unreadCount: 5,
|
|
latestPosts: [
|
|
{
|
|
id: 'post-1',
|
|
title: 'New Season Announcement',
|
|
date: '2024-01-14T10:00:00.000Z',
|
|
},
|
|
],
|
|
},
|
|
friends: [
|
|
{
|
|
id: 'friend-1',
|
|
name: 'Friend 1',
|
|
avatarUrl: 'https://example.com/friend1.jpg',
|
|
status: 'online',
|
|
},
|
|
],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(mockOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockOverview);
|
|
expect(mockDashboardApiClient.getDashboardOverview).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should successfully fetch analytics metrics', async () => {
|
|
const mockMetrics: GetAnalyticsMetricsOutputDTO = {
|
|
pageViews: 15000,
|
|
uniqueVisitors: 8500,
|
|
averageSessionDuration: 180,
|
|
bounceRate: 0.35,
|
|
};
|
|
|
|
mockAnalyticsApiClient.getAnalyticsMetrics.mockResolvedValue(mockMetrics);
|
|
|
|
const result = await service.getAnalyticsMetrics();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockMetrics);
|
|
expect(mockAnalyticsApiClient.getAnalyticsMetrics).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('failure modes', () => {
|
|
it('should handle not found errors', async () => {
|
|
const error = new ApiError(
|
|
'Dashboard not found',
|
|
'NOT_FOUND',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 404,
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'notFound',
|
|
message: 'Dashboard not found',
|
|
});
|
|
});
|
|
|
|
it('should handle unauthorized errors', async () => {
|
|
const error = new ApiError(
|
|
'Unauthorized access',
|
|
'AUTH_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 401,
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'unauthorized',
|
|
message: 'Unauthorized access',
|
|
});
|
|
});
|
|
|
|
it('should handle server errors', async () => {
|
|
const error = new ApiError(
|
|
'Internal server error',
|
|
'SERVER_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 500,
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'serverError',
|
|
message: 'Internal server error',
|
|
});
|
|
});
|
|
|
|
it('should handle network errors', async () => {
|
|
const error = new ApiError(
|
|
'Network error: Unable to reach the API server',
|
|
'NETWORK_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'networkError',
|
|
message: 'Network error: Unable to reach the API server',
|
|
});
|
|
});
|
|
|
|
it('should handle timeout errors', async () => {
|
|
const error = new ApiError(
|
|
'Request timed out after 30 seconds',
|
|
'TIMEOUT_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'networkError',
|
|
message: 'Request timed out after 30 seconds',
|
|
});
|
|
});
|
|
|
|
it('should handle unknown errors', async () => {
|
|
const error = new Error('Something went wrong');
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'unknown',
|
|
message: 'Something went wrong',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('retries', () => {
|
|
it('should retry on network failure', async () => {
|
|
const mockOverview: DashboardOverviewDTO = {
|
|
currentDriver: undefined,
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
const error = new ApiError(
|
|
'Network error',
|
|
'NETWORK_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
// First call fails, second succeeds
|
|
mockDashboardApiClient.getDashboardOverview
|
|
.mockRejectedValueOnce(error)
|
|
.mockResolvedValueOnce(mockOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockOverview);
|
|
expect(mockDashboardApiClient.getDashboardOverview).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('should retry on timeout', async () => {
|
|
const mockOverview: DashboardOverviewDTO = {
|
|
currentDriver: undefined,
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
const error = new ApiError(
|
|
'Request timed out after 30 seconds',
|
|
'TIMEOUT_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
// First call times out, second succeeds
|
|
mockDashboardApiClient.getDashboardOverview
|
|
.mockRejectedValueOnce(error)
|
|
.mockResolvedValueOnce(mockOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockOverview);
|
|
expect(mockDashboardApiClient.getDashboardOverview).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
describe('fallback logic', () => {
|
|
it('should use fallback when primary API fails', async () => {
|
|
const error = new ApiError(
|
|
'Unable to connect to server',
|
|
'NETWORK_ERROR',
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
// The service should return an error result, not fallback data
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'networkError',
|
|
message: 'Unable to connect to server',
|
|
});
|
|
});
|
|
|
|
it('should handle partial data gracefully', async () => {
|
|
const mockOverview: DashboardOverviewDTO = {
|
|
currentDriver: undefined, // Missing driver data
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(mockOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const overview = result.unwrap();
|
|
expect(overview.currentDriver).toBeUndefined();
|
|
expect(overview.myUpcomingRaces).toHaveLength(0);
|
|
expect(overview.activeLeaguesCount).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('aggregation logic', () => {
|
|
it('should aggregate dashboard data correctly', async () => {
|
|
const mockOverview: DashboardOverviewDTO = {
|
|
currentDriver: {
|
|
id: 'driver-123',
|
|
name: 'Test Driver',
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
rating: 1500,
|
|
rank: 10,
|
|
},
|
|
myUpcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
{
|
|
id: 'race-2',
|
|
name: 'Race 2',
|
|
date: '2024-01-16T10:00:00.000Z',
|
|
track: 'Track 2',
|
|
league: 'League 2',
|
|
},
|
|
],
|
|
otherUpcomingRaces: [
|
|
{
|
|
id: 'race-3',
|
|
name: 'Race 3',
|
|
date: '2024-01-17T10:00:00.000Z',
|
|
track: 'Track 3',
|
|
league: 'League 3',
|
|
},
|
|
],
|
|
upcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
{
|
|
id: 'race-2',
|
|
name: 'Race 2',
|
|
date: '2024-01-16T10:00:00.000Z',
|
|
track: 'Track 2',
|
|
league: 'League 2',
|
|
},
|
|
{
|
|
id: 'race-3',
|
|
name: 'Race 3',
|
|
date: '2024-01-17T10:00:00.000Z',
|
|
track: 'Track 3',
|
|
league: 'League 3',
|
|
},
|
|
],
|
|
activeLeaguesCount: 3,
|
|
nextRace: {
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
recentResults: [
|
|
{
|
|
raceId: 'race-0',
|
|
position: 5,
|
|
points: 15,
|
|
date: '2024-01-10T10:00:00.000Z',
|
|
},
|
|
{
|
|
raceId: 'race--1',
|
|
position: 3,
|
|
points: 20,
|
|
date: '2024-01-09T10:00:00.000Z',
|
|
},
|
|
],
|
|
leagueStandingsSummaries: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
position: 3,
|
|
points: 150,
|
|
},
|
|
{
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
position: 1,
|
|
points: 200,
|
|
},
|
|
],
|
|
feedSummary: {
|
|
unreadCount: 5,
|
|
latestPosts: [
|
|
{
|
|
id: 'post-1',
|
|
title: 'New Season Announcement',
|
|
date: '2024-01-14T10:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'post-2',
|
|
title: 'Race Results Published',
|
|
date: '2024-01-13T10:00:00.000Z',
|
|
},
|
|
],
|
|
},
|
|
friends: [
|
|
{
|
|
id: 'friend-1',
|
|
name: 'Friend 1',
|
|
avatarUrl: 'https://example.com/friend1.jpg',
|
|
status: 'online',
|
|
},
|
|
{
|
|
id: 'friend-2',
|
|
name: 'Friend 2',
|
|
avatarUrl: 'https://example.com/friend2.jpg',
|
|
status: 'offline',
|
|
},
|
|
],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(mockOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const overview = result.unwrap();
|
|
|
|
// Verify aggregation
|
|
expect(overview.currentDriver).toBeDefined();
|
|
expect(overview.currentDriver.id).toBe('driver-123');
|
|
expect(overview.currentDriver.rating).toBe(1500);
|
|
|
|
// Verify race aggregation
|
|
expect(overview.myUpcomingRaces).toHaveLength(2);
|
|
expect(overview.otherUpcomingRaces).toHaveLength(1);
|
|
expect(overview.upcomingRaces).toHaveLength(3);
|
|
|
|
// Verify league aggregation
|
|
expect(overview.activeLeaguesCount).toBe(3);
|
|
expect(overview.leagueStandingsSummaries).toHaveLength(2);
|
|
|
|
// Verify results aggregation
|
|
expect(overview.recentResults).toHaveLength(2);
|
|
const totalPoints = overview.recentResults.reduce((sum, r) => sum + r.points, 0);
|
|
expect(totalPoints).toBe(35);
|
|
|
|
// Verify feed aggregation
|
|
expect(overview.feedSummary.unreadCount).toBe(5);
|
|
expect(overview.feedSummary.latestPosts).toHaveLength(2);
|
|
|
|
// Verify friends aggregation
|
|
expect(overview.friends).toHaveLength(2);
|
|
const onlineFriends = overview.friends.filter(f => f.status === 'online').length;
|
|
expect(onlineFriends).toBe(1);
|
|
});
|
|
|
|
it('should combine analytics metrics from multiple sources', async () => {
|
|
const mockMetrics: GetAnalyticsMetricsOutputDTO = {
|
|
pageViews: 15000,
|
|
uniqueVisitors: 8500,
|
|
averageSessionDuration: 180,
|
|
bounceRate: 0.35,
|
|
};
|
|
|
|
mockAnalyticsApiClient.getAnalyticsMetrics.mockResolvedValue(mockMetrics);
|
|
|
|
const result = await service.getAnalyticsMetrics();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const metrics = result.unwrap();
|
|
|
|
// Verify metrics are returned correctly
|
|
expect(metrics.pageViews).toBe(15000);
|
|
expect(metrics.uniqueVisitors).toBe(8500);
|
|
expect(metrics.averageSessionDuration).toBe(180);
|
|
expect(metrics.bounceRate).toBe(0.35);
|
|
|
|
// Verify derived metrics
|
|
const pageViewsPerVisitor = metrics.pageViews / metrics.uniqueVisitors;
|
|
expect(pageViewsPerVisitor).toBeCloseTo(1.76, 2);
|
|
|
|
const bounceRatePercentage = metrics.bounceRate * 100;
|
|
expect(bounceRatePercentage).toBe(35);
|
|
});
|
|
});
|
|
|
|
describe('decision branches', () => {
|
|
it('should handle different error types correctly', async () => {
|
|
const errorTypes = [
|
|
{ type: 'NOT_FOUND', expectedErrorType: 'notFound' },
|
|
{ type: 'AUTH_ERROR', expectedErrorType: 'unauthorized' },
|
|
{ type: 'SERVER_ERROR', expectedErrorType: 'serverError' },
|
|
{ type: 'NETWORK_ERROR', expectedErrorType: 'networkError' },
|
|
{ type: 'TIMEOUT_ERROR', expectedErrorType: 'networkError' },
|
|
];
|
|
|
|
for (const { type, expectedErrorType } of errorTypes) {
|
|
const error = new ApiError(
|
|
`Error of type ${type}`,
|
|
type as any,
|
|
{
|
|
endpoint: '/dashboard/overview',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockRejectedValue(error);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: expectedErrorType,
|
|
message: `Error of type ${type}`,
|
|
});
|
|
}
|
|
});
|
|
|
|
it('should handle different API response formats', async () => {
|
|
// Test with minimal response
|
|
const minimalOverview: DashboardOverviewDTO = {
|
|
currentDriver: undefined,
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(minimalOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const overview = result.unwrap();
|
|
expect(overview.activeLeaguesCount).toBe(0);
|
|
expect(overview.upcomingRaces).toHaveLength(0);
|
|
|
|
// Test with full response
|
|
const fullOverview: DashboardOverviewDTO = {
|
|
currentDriver: {
|
|
id: 'driver-123',
|
|
name: 'Test Driver',
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
rating: 1500,
|
|
rank: 10,
|
|
},
|
|
myUpcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [
|
|
{
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
],
|
|
activeLeaguesCount: 1,
|
|
nextRace: {
|
|
id: 'race-1',
|
|
name: 'Race 1',
|
|
date: '2024-01-15T10:00:00.000Z',
|
|
track: 'Track 1',
|
|
league: 'League 1',
|
|
},
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(fullOverview);
|
|
|
|
const result2 = await service.getDashboardOverview();
|
|
|
|
expect(result2.isOk()).toBe(true);
|
|
const overview2 = result2.unwrap();
|
|
expect(overview2.currentDriver).toBeDefined();
|
|
expect(overview2.currentDriver.id).toBe('driver-123');
|
|
expect(overview2.activeLeaguesCount).toBe(1);
|
|
});
|
|
|
|
it('should handle different user permission levels', async () => {
|
|
// Test with driver data (normal user)
|
|
const driverOverview: DashboardOverviewDTO = {
|
|
currentDriver: {
|
|
id: 'driver-123',
|
|
name: 'Test Driver',
|
|
avatarUrl: 'https://example.com/avatar.jpg',
|
|
rating: 1500,
|
|
rank: 10,
|
|
},
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(driverOverview);
|
|
|
|
const result = await service.getDashboardOverview();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const overview = result.unwrap();
|
|
expect(overview.currentDriver).toBeDefined();
|
|
expect(overview.currentDriver.id).toBe('driver-123');
|
|
|
|
// Test without driver data (guest user or no driver assigned)
|
|
const guestOverview: DashboardOverviewDTO = {
|
|
currentDriver: undefined,
|
|
myUpcomingRaces: [],
|
|
otherUpcomingRaces: [],
|
|
upcomingRaces: [],
|
|
activeLeaguesCount: 0,
|
|
recentResults: [],
|
|
leagueStandingsSummaries: [],
|
|
feedSummary: { unreadCount: 0, latestPosts: [] },
|
|
friends: [],
|
|
};
|
|
|
|
mockDashboardApiClient.getDashboardOverview.mockResolvedValue(guestOverview);
|
|
|
|
const result2 = await service.getDashboardOverview();
|
|
|
|
expect(result2.isOk()).toBe(true);
|
|
const overview2 = result2.unwrap();
|
|
expect(overview2.currentDriver).toBeUndefined();
|
|
});
|
|
});
|
|
});
|