998 lines
29 KiB
TypeScript
998 lines
29 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { SponsorService } from '@/lib/services/sponsors/SponsorService';
|
|
import { SponsorsApiClient } from '@/lib/api/sponsors/SponsorsApiClient';
|
|
import { ApiError } from '@/lib/api/base/ApiError';
|
|
import type { SponsorDashboardDTO } from '@/lib/types/generated/SponsorDashboardDTO';
|
|
import type { SponsorSponsorshipsDTO } from '@/lib/types/generated/SponsorSponsorshipsDTO';
|
|
|
|
// Mock the API client
|
|
vi.mock('@/lib/api/sponsors/SponsorsApiClient');
|
|
|
|
describe('SponsorService', () => {
|
|
let service: SponsorService;
|
|
let mockApiClient: any;
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
service = new SponsorService();
|
|
mockApiClient = (service as any).apiClient;
|
|
});
|
|
|
|
describe('happy paths', () => {
|
|
it('should successfully fetch sponsor dashboard', async () => {
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
},
|
|
],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '1',
|
|
teams: '3',
|
|
drivers: '15',
|
|
races: '8',
|
|
platform: ['web', 'mobile'],
|
|
recentActivity: [
|
|
{
|
|
id: 'activity-1',
|
|
type: 'sponsorship_created',
|
|
description: 'New sponsorship created',
|
|
timestamp: '2024-01-15T10:00:00.000Z',
|
|
},
|
|
],
|
|
upcomingRenewals: [
|
|
{
|
|
sponsorshipId: 'sponsorship-1',
|
|
sponsorName: 'Test Sponsor',
|
|
leagueName: 'League 1',
|
|
renewalDate: '2024-02-01T00:00:00.000Z',
|
|
amount: 10000,
|
|
},
|
|
],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockDashboard);
|
|
expect(mockApiClient.getDashboard).toHaveBeenCalledWith('sponsor-123');
|
|
});
|
|
|
|
it('should successfully fetch sponsor sponsorships', async () => {
|
|
const mockSponsorships: SponsorSponsorshipsDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
sponsorships: [
|
|
{
|
|
id: 'sponsorship-1',
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
startDate: '2024-01-01T00:00:00.000Z',
|
|
endDate: '2024-12-31T23:59:59.999Z',
|
|
},
|
|
],
|
|
summary: {},
|
|
totalSponsorships: '1',
|
|
activeSponsorships: '1',
|
|
totalInvestment: '10000',
|
|
totalPlatformFees: '500',
|
|
currency: 'USD',
|
|
};
|
|
|
|
mockApiClient.getSponsorships.mockResolvedValue(mockSponsorships);
|
|
|
|
const result = await service.getSponsorSponsorships('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockSponsorships);
|
|
expect(mockApiClient.getSponsorships).toHaveBeenCalledWith('sponsor-123');
|
|
});
|
|
|
|
it('should successfully fetch available leagues', async () => {
|
|
const mockLeagues = [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
maxSponsors: 5,
|
|
currentSponsors: 3,
|
|
basePrice: 5000,
|
|
tier: 'premium',
|
|
},
|
|
{
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
maxSponsors: 10,
|
|
currentSponsors: 2,
|
|
basePrice: 2000,
|
|
tier: 'standard',
|
|
},
|
|
];
|
|
|
|
mockApiClient.getAvailableLeagues.mockResolvedValue(mockLeagues);
|
|
|
|
const result = await service.getAvailableLeagues();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockLeagues);
|
|
expect(mockApiClient.getAvailableLeagues).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should successfully fetch league detail', async () => {
|
|
const mockLeagueDetail = {
|
|
league: {
|
|
id: 'league-1',
|
|
name: 'League 1',
|
|
description: 'Test league',
|
|
maxSponsors: 5,
|
|
basePrice: 5000,
|
|
tier: 'premium',
|
|
},
|
|
drivers: [
|
|
{ id: 'driver-1', name: 'Driver 1', rating: 1500 },
|
|
{ id: 'driver-2', name: 'Driver 2', rating: 1400 },
|
|
],
|
|
races: [
|
|
{ id: 'race-1', name: 'Race 1', date: '2024-01-15T10:00:00.000Z' },
|
|
],
|
|
};
|
|
|
|
mockApiClient.getLeagueDetail.mockResolvedValue(mockLeagueDetail);
|
|
|
|
const result = await service.getLeagueDetail('league-1');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockLeagueDetail);
|
|
expect(mockApiClient.getLeagueDetail).toHaveBeenCalledWith('league-1');
|
|
});
|
|
});
|
|
|
|
describe('failure modes', () => {
|
|
it('should handle sponsor dashboard not found', async () => {
|
|
const error = new ApiError(
|
|
'Sponsor dashboard not found',
|
|
'NOT_FOUND',
|
|
{
|
|
endpoint: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 404,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getDashboard.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'notFound',
|
|
message: 'Sponsor dashboard not found',
|
|
});
|
|
});
|
|
|
|
it('should handle sponsor sponsorships not found', async () => {
|
|
const error = new ApiError(
|
|
'Sponsor sponsorships not found',
|
|
'NOT_FOUND',
|
|
{
|
|
endpoint: '/sponsors/sponsor-123/sponsorships',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 404,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getSponsorships.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorSponsorships('sponsor-123');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'notFound',
|
|
message: 'Sponsor sponsorships not found',
|
|
});
|
|
});
|
|
|
|
it('should handle server errors', async () => {
|
|
const error = new ApiError(
|
|
'Internal server error',
|
|
'SERVER_ERROR',
|
|
{
|
|
endpoint: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 500,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getDashboard.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
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: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
mockApiClient.getDashboard.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'serverError',
|
|
message: 'Network error: Unable to reach the API server',
|
|
});
|
|
});
|
|
|
|
it('should handle invalid sponsor ID', async () => {
|
|
const error = new ApiError(
|
|
'Invalid sponsor ID format',
|
|
'VALIDATION_ERROR',
|
|
{
|
|
endpoint: '/sponsors/invalid-id/dashboard',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 400,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getDashboard.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorDashboard('invalid-id');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'serverError',
|
|
message: 'Invalid sponsor ID format',
|
|
});
|
|
});
|
|
|
|
it('should handle invalid league ID', async () => {
|
|
const error = new ApiError(
|
|
'Invalid league ID format',
|
|
'VALIDATION_ERROR',
|
|
{
|
|
endpoint: '/sponsors/leagues/invalid-id/detail',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 400,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getLeagueDetail.mockRejectedValue(error);
|
|
|
|
const result = await service.getLeagueDetail('invalid-id');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'serverError',
|
|
message: 'Invalid league ID format',
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('retries', () => {
|
|
it('should retry on transient network failures', async () => {
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
const error = new ApiError(
|
|
'Network error',
|
|
'NETWORK_ERROR',
|
|
{
|
|
endpoint: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
// First call fails, second succeeds
|
|
mockApiClient.getDashboard
|
|
.mockRejectedValueOnce(error)
|
|
.mockResolvedValueOnce(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockDashboard);
|
|
expect(mockApiClient.getDashboard).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('should retry on timeout when fetching dashboard', async () => {
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
const error = new ApiError(
|
|
'Request timed out after 30 seconds',
|
|
'TIMEOUT_ERROR',
|
|
{
|
|
endpoint: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
}
|
|
);
|
|
|
|
// First call times out, second succeeds
|
|
mockApiClient.getDashboard
|
|
.mockRejectedValueOnce(error)
|
|
.mockResolvedValueOnce(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
expect(result.unwrap()).toEqual(mockDashboard);
|
|
expect(mockApiClient.getDashboard).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
describe('fallback logic', () => {
|
|
it('should handle missing dashboard data gracefully', async () => {
|
|
const error = new ApiError(
|
|
'Sponsor dashboard not found',
|
|
'NOT_FOUND',
|
|
{
|
|
endpoint: '/sponsors/dashboard/sponsor-123',
|
|
method: 'GET',
|
|
timestamp: new Date().toISOString(),
|
|
statusCode: 404,
|
|
}
|
|
);
|
|
|
|
mockApiClient.getDashboard.mockRejectedValue(error);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isErr()).toBe(true);
|
|
expect(result.getError()).toEqual({
|
|
type: 'notFound',
|
|
message: 'Sponsor dashboard not found',
|
|
});
|
|
});
|
|
|
|
it('should use cached data when available', async () => {
|
|
// Note: The current implementation doesn't have caching, but we can test
|
|
// that the service handles the API response correctly
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const dashboard = result.unwrap();
|
|
expect(dashboard.sponsorId).toBe('sponsor-123');
|
|
expect(dashboard.sponsorName).toBe('Test Sponsor');
|
|
});
|
|
|
|
it('should handle partial sponsor data', async () => {
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const dashboard = result.unwrap();
|
|
expect(dashboard.sponsoredLeagues).toHaveLength(0);
|
|
expect(dashboard.recentActivity).toHaveLength(0);
|
|
expect(dashboard.upcomingRenewals).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe('aggregation logic', () => {
|
|
it('should aggregate sponsor dashboard data correctly', async () => {
|
|
const mockDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
},
|
|
{
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
status: 'active',
|
|
investment: 15000,
|
|
},
|
|
],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '2',
|
|
teams: '5',
|
|
drivers: '20',
|
|
races: '12',
|
|
platform: ['web', 'mobile', 'desktop'],
|
|
recentActivity: [
|
|
{
|
|
id: 'activity-1',
|
|
type: 'sponsorship_created',
|
|
description: 'New sponsorship created',
|
|
timestamp: '2024-01-15T10:00:00.000Z',
|
|
},
|
|
{
|
|
id: 'activity-2',
|
|
type: 'investment_updated',
|
|
description: 'Investment increased',
|
|
timestamp: '2024-01-14T10:00:00.000Z',
|
|
},
|
|
],
|
|
upcomingRenewals: [
|
|
{
|
|
sponsorshipId: 'sponsorship-1',
|
|
sponsorName: 'Test Sponsor',
|
|
leagueName: 'League 1',
|
|
renewalDate: '2024-02-01T00:00:00.000Z',
|
|
amount: 10000,
|
|
},
|
|
{
|
|
sponsorshipId: 'sponsorship-2',
|
|
sponsorName: 'Test Sponsor',
|
|
leagueName: 'League 2',
|
|
renewalDate: '2024-02-15T00:00:00.000Z',
|
|
amount: 15000,
|
|
},
|
|
],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(mockDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const dashboard = result.unwrap();
|
|
|
|
// Verify aggregation
|
|
expect(dashboard.sponsorId).toBe('sponsor-123');
|
|
expect(dashboard.sponsorName).toBe('Test Sponsor');
|
|
|
|
// Verify metrics aggregation
|
|
expect(dashboard.metrics.totalInvestment).toBe(50000);
|
|
expect(dashboard.metrics.activeSponsorships).toBe(5);
|
|
expect(dashboard.metrics.totalSponsorships).toBe(10);
|
|
expect(dashboard.metrics.totalPlatformFees).toBe(2500);
|
|
|
|
// Verify sponsored leagues aggregation
|
|
expect(dashboard.sponsoredLeagues).toHaveLength(2);
|
|
const totalLeagueInvestment = dashboard.sponsoredLeagues.reduce(
|
|
(sum, league) => sum + league.investment,
|
|
0
|
|
);
|
|
expect(totalLeagueInvestment).toBe(25000);
|
|
|
|
// Verify investment aggregation
|
|
expect(dashboard.investment.total).toBe(50000);
|
|
expect(dashboard.investment.monthly).toBe(5000);
|
|
expect(dashboard.investment.yearly).toBe(60000);
|
|
|
|
// Verify activity aggregation
|
|
expect(dashboard.recentActivity).toHaveLength(2);
|
|
expect(dashboard.upcomingRenewals).toHaveLength(2);
|
|
|
|
// Verify platform aggregation
|
|
expect(dashboard.platform).toHaveLength(3);
|
|
});
|
|
|
|
it('should combine sponsorships with league information', async () => {
|
|
const mockSponsorships: SponsorSponsorshipsDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
sponsorships: [
|
|
{
|
|
id: 'sponsorship-1',
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
startDate: '2024-01-01T00:00:00.000Z',
|
|
endDate: '2024-12-31T23:59:59.999Z',
|
|
},
|
|
{
|
|
id: 'sponsorship-2',
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
status: 'active',
|
|
investment: 15000,
|
|
startDate: '2024-01-01T00:00:00.000Z',
|
|
endDate: '2024-12-31T23:59:59.999Z',
|
|
},
|
|
],
|
|
summary: {},
|
|
totalSponsorships: '2',
|
|
activeSponsorships: '2',
|
|
totalInvestment: '25000',
|
|
totalPlatformFees: '1250',
|
|
currency: 'USD',
|
|
};
|
|
|
|
mockApiClient.getSponsorships.mockResolvedValue(mockSponsorships);
|
|
|
|
const result = await service.getSponsorSponsorships('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const sponsorships = result.unwrap();
|
|
|
|
// Verify sponsorships aggregation
|
|
expect(sponsorships.sponsorships).toHaveLength(2);
|
|
expect(sponsorships.totalSponsorships).toBe('2');
|
|
expect(sponsorships.activeSponsorships).toBe('2');
|
|
|
|
// Verify investment aggregation
|
|
const totalInvestment = sponsorships.sponsorships.reduce(
|
|
(sum, s) => sum + s.investment,
|
|
0
|
|
);
|
|
expect(totalInvestment).toBe(25000);
|
|
|
|
// Verify league information is combined
|
|
expect(sponsorships.sponsorships[0].leagueName).toBe('League 1');
|
|
expect(sponsorships.sponsorships[1].leagueName).toBe('League 2');
|
|
});
|
|
|
|
it('should aggregate available leagues with pricing data', async () => {
|
|
const mockLeagues = [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
maxSponsors: 5,
|
|
currentSponsors: 3,
|
|
basePrice: 5000,
|
|
tier: 'premium',
|
|
},
|
|
{
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
maxSponsors: 10,
|
|
currentSponsors: 2,
|
|
basePrice: 2000,
|
|
tier: 'standard',
|
|
},
|
|
{
|
|
leagueId: 'league-3',
|
|
leagueName: 'League 3',
|
|
maxSponsors: 20,
|
|
currentSponsors: 5,
|
|
basePrice: 1000,
|
|
tier: 'basic',
|
|
},
|
|
];
|
|
|
|
mockApiClient.getAvailableLeagues.mockResolvedValue(mockLeagues);
|
|
|
|
const result = await service.getAvailableLeagues();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const leagues = result.unwrap();
|
|
|
|
// Verify aggregation
|
|
expect(leagues).toHaveLength(3);
|
|
|
|
// Verify pricing aggregation
|
|
const totalBasePrice = leagues.reduce((sum, league) => sum + league.basePrice, 0);
|
|
expect(totalBasePrice).toBe(8000);
|
|
|
|
// Verify availability aggregation
|
|
const availableLeagues = leagues.filter(
|
|
(league) => league.currentSponsors < league.maxSponsors
|
|
);
|
|
expect(availableLeagues).toHaveLength(3);
|
|
|
|
// Verify tier distribution
|
|
const premiumLeagues = leagues.filter((league) => league.tier === 'premium');
|
|
const standardLeagues = leagues.filter((league) => league.tier === 'standard');
|
|
const basicLeagues = leagues.filter((league) => league.tier === 'basic');
|
|
|
|
expect(premiumLeagues).toHaveLength(1);
|
|
expect(standardLeagues).toHaveLength(1);
|
|
expect(basicLeagues).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe('decision branches', () => {
|
|
it('should handle different sponsor permission levels', async () => {
|
|
// Test with full access sponsor
|
|
const fullAccessDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
},
|
|
],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '1',
|
|
teams: '3',
|
|
drivers: '15',
|
|
races: '8',
|
|
platform: ['web', 'mobile'],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(fullAccessDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const dashboard = result.unwrap();
|
|
expect(dashboard.sponsoredLeagues).toHaveLength(1);
|
|
expect(dashboard.recentActivity).toHaveLength(0);
|
|
|
|
// Test with limited access sponsor
|
|
const limitedAccessDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-456',
|
|
sponsorName: 'Limited Sponsor',
|
|
metrics: {
|
|
totalInvestment: 10000,
|
|
activeSponsorships: 1,
|
|
totalSponsorships: 1,
|
|
totalPlatformFees: 500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 10000,
|
|
monthly: 1000,
|
|
yearly: 12000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(limitedAccessDashboard);
|
|
|
|
const result2 = await service.getSponsorDashboard('sponsor-456');
|
|
|
|
expect(result2.isOk()).toBe(true);
|
|
const dashboard2 = result2.unwrap();
|
|
expect(dashboard2.sponsoredLeagues).toHaveLength(0);
|
|
expect(dashboard2.metrics.totalInvestment).toBe(10000);
|
|
});
|
|
|
|
it('should handle different API response formats', async () => {
|
|
// Test with minimal response
|
|
const minimalDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 0,
|
|
activeSponsorships: 0,
|
|
totalSponsorships: 0,
|
|
totalPlatformFees: 0,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [],
|
|
investment: {
|
|
total: 0,
|
|
monthly: 0,
|
|
yearly: 0,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '0',
|
|
teams: '0',
|
|
drivers: '0',
|
|
races: '0',
|
|
platform: [],
|
|
recentActivity: [],
|
|
upcomingRenewals: [],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(minimalDashboard);
|
|
|
|
const result = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const dashboard = result.unwrap();
|
|
expect(dashboard.metrics.totalInvestment).toBe(0);
|
|
expect(dashboard.sponsoredLeagues).toHaveLength(0);
|
|
|
|
// Test with full response
|
|
const fullDashboard: SponsorDashboardDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
metrics: {
|
|
totalInvestment: 50000,
|
|
activeSponsorships: 5,
|
|
totalSponsorships: 10,
|
|
totalPlatformFees: 2500,
|
|
currency: 'USD',
|
|
},
|
|
sponsoredLeagues: [
|
|
{
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'active',
|
|
investment: 10000,
|
|
},
|
|
],
|
|
investment: {
|
|
total: 50000,
|
|
monthly: 5000,
|
|
yearly: 60000,
|
|
},
|
|
sponsorships: {},
|
|
leagues: '1',
|
|
teams: '3',
|
|
drivers: '15',
|
|
races: '8',
|
|
platform: ['web', 'mobile'],
|
|
recentActivity: [
|
|
{
|
|
id: 'activity-1',
|
|
type: 'sponsorship_created',
|
|
description: 'New sponsorship created',
|
|
timestamp: '2024-01-15T10:00:00.000Z',
|
|
},
|
|
],
|
|
upcomingRenewals: [
|
|
{
|
|
sponsorshipId: 'sponsorship-1',
|
|
sponsorName: 'Test Sponsor',
|
|
leagueName: 'League 1',
|
|
renewalDate: '2024-02-01T00:00:00.000Z',
|
|
amount: 10000,
|
|
},
|
|
],
|
|
};
|
|
|
|
mockApiClient.getDashboard.mockResolvedValue(fullDashboard);
|
|
|
|
const result2 = await service.getSponsorDashboard('sponsor-123');
|
|
|
|
expect(result2.isOk()).toBe(true);
|
|
const dashboard2 = result2.unwrap();
|
|
expect(dashboard2.metrics.totalInvestment).toBe(50000);
|
|
expect(dashboard2.sponsoredLeagues).toHaveLength(1);
|
|
expect(dashboard2.recentActivity).toHaveLength(1);
|
|
expect(dashboard2.upcomingRenewals).toHaveLength(1);
|
|
});
|
|
|
|
it('should handle empty sponsorships list', async () => {
|
|
const mockSponsorships: SponsorSponsorshipsDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
sponsorships: [],
|
|
summary: {},
|
|
totalSponsorships: '0',
|
|
activeSponsorships: '0',
|
|
totalInvestment: '0',
|
|
totalPlatformFees: '0',
|
|
currency: 'USD',
|
|
};
|
|
|
|
mockApiClient.getSponsorships.mockResolvedValue(mockSponsorships);
|
|
|
|
const result = await service.getSponsorSponsorships('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const sponsorships = result.unwrap();
|
|
expect(sponsorships.sponsorships).toHaveLength(0);
|
|
expect(sponsorships.totalSponsorships).toBe('0');
|
|
expect(sponsorships.activeSponsorships).toBe('0');
|
|
});
|
|
|
|
it('should handle empty available leagues list', async () => {
|
|
const mockLeagues: any[] = [];
|
|
|
|
mockApiClient.getAvailableLeagues.mockResolvedValue(mockLeagues);
|
|
|
|
const result = await service.getAvailableLeagues();
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const leagues = result.unwrap();
|
|
expect(leagues).toHaveLength(0);
|
|
});
|
|
|
|
it('should handle expired sponsorships', async () => {
|
|
const mockSponsorships: SponsorSponsorshipsDTO = {
|
|
sponsorId: 'sponsor-123',
|
|
sponsorName: 'Test Sponsor',
|
|
sponsorships: [
|
|
{
|
|
id: 'sponsorship-1',
|
|
leagueId: 'league-1',
|
|
leagueName: 'League 1',
|
|
status: 'expired',
|
|
investment: 10000,
|
|
startDate: '2023-01-01T00:00:00.000Z',
|
|
endDate: '2023-12-31T23:59:59.999Z',
|
|
},
|
|
{
|
|
id: 'sponsorship-2',
|
|
leagueId: 'league-2',
|
|
leagueName: 'League 2',
|
|
status: 'active',
|
|
investment: 15000,
|
|
startDate: '2024-01-01T00:00:00.000Z',
|
|
endDate: '2024-12-31T23:59:59.999Z',
|
|
},
|
|
],
|
|
summary: {},
|
|
totalSponsorships: '2',
|
|
activeSponsorships: '1',
|
|
totalInvestment: '25000',
|
|
totalPlatformFees: '1250',
|
|
currency: 'USD',
|
|
};
|
|
|
|
mockApiClient.getSponsorships.mockResolvedValue(mockSponsorships);
|
|
|
|
const result = await service.getSponsorSponsorships('sponsor-123');
|
|
|
|
expect(result.isOk()).toBe(true);
|
|
const sponsorships = result.unwrap();
|
|
|
|
// Verify expired sponsorship is included
|
|
expect(sponsorships.sponsorships).toHaveLength(2);
|
|
expect(sponsorships.sponsorships[0].status).toBe('expired');
|
|
expect(sponsorships.sponsorships[1].status).toBe('active');
|
|
|
|
// Verify active sponsorships count
|
|
expect(sponsorships.activeSponsorships).toBe('1');
|
|
});
|
|
});
|
|
});
|