import { describe, it, expect, vi, beforeEach } from 'vitest'; import { SponsorService } from './SponsorService'; import type { SponsorsApiClient } from '../../api/sponsors/SponsorsApiClient'; import type { SponsorListPresenter } from '../../presenters/SponsorListPresenter'; import type { SponsorDashboardPresenter } from '../../presenters/SponsorDashboardPresenter'; import type { SponsorSponsorshipsPresenter } from '../../presenters/SponsorSponsorshipsPresenter'; import type { GetSponsorsOutputDto, SponsorDashboardDto, SponsorSponsorshipsDto, CreateSponsorInputDto, CreateSponsorOutputDto, GetEntitySponsorshipPricingResultDto, } from '../../dtos'; import type { SponsorViewModel, SponsorDashboardViewModel, SponsorSponsorshipsViewModel } from '../../view-models'; describe('SponsorService', () => { let service: SponsorService; let mockApiClient: SponsorsApiClient; let mockSponsorListPresenter: SponsorListPresenter; let mockSponsorDashboardPresenter: SponsorDashboardPresenter; let mockSponsorSponsorshipsPresenter: SponsorSponsorshipsPresenter; beforeEach(() => { mockApiClient = { getAll: vi.fn(), getDashboard: vi.fn(), getSponsorships: vi.fn(), create: vi.fn(), getPricing: vi.fn(), } as unknown as SponsorsApiClient; mockSponsorListPresenter = { present: vi.fn(), } as unknown as SponsorListPresenter; mockSponsorDashboardPresenter = { present: vi.fn(), } as unknown as SponsorDashboardPresenter; mockSponsorSponsorshipsPresenter = { present: vi.fn(), } as unknown as SponsorSponsorshipsPresenter; service = new SponsorService( mockApiClient, mockSponsorListPresenter, mockSponsorDashboardPresenter, mockSponsorSponsorshipsPresenter ); }); describe('constructor', () => { it('should create instance with injected dependencies', () => { expect(service).toBeInstanceOf(SponsorService); }); }); describe('getAllSponsors', () => { it('should fetch all sponsors from API and transform via presenter', async () => { // Arrange const mockDto: GetSponsorsOutputDto = { sponsors: [ { id: 'sponsor-1', name: 'Sponsor Alpha', logoUrl: 'https://example.com/logo1.png', websiteUrl: 'https://alpha.com', }, { id: 'sponsor-2', name: 'Sponsor Beta', logoUrl: 'https://example.com/logo2.png', websiteUrl: 'https://beta.com', }, ], }; const mockViewModels: SponsorViewModel[] = [ { id: 'sponsor-1', name: 'Sponsor Alpha', logoUrl: 'https://example.com/logo1.png', websiteUrl: 'https://alpha.com', } as SponsorViewModel, { id: 'sponsor-2', name: 'Sponsor Beta', logoUrl: 'https://example.com/logo2.png', websiteUrl: 'https://beta.com', } as SponsorViewModel, ]; vi.mocked(mockApiClient.getAll).mockResolvedValue(mockDto); vi.mocked(mockSponsorListPresenter.present).mockReturnValue(mockViewModels); // Act const result = await service.getAllSponsors(); // Assert expect(mockApiClient.getAll).toHaveBeenCalled(); expect(mockSponsorListPresenter.present).toHaveBeenCalledWith(mockDto); expect(result).toEqual(mockViewModels); }); it('should handle empty sponsors list', async () => { // Arrange const mockDto: GetSponsorsOutputDto = { sponsors: [], }; const mockViewModels: SponsorViewModel[] = []; vi.mocked(mockApiClient.getAll).mockResolvedValue(mockDto); vi.mocked(mockSponsorListPresenter.present).mockReturnValue(mockViewModels); // Act const result = await service.getAllSponsors(); // Assert expect(mockApiClient.getAll).toHaveBeenCalled(); expect(mockSponsorListPresenter.present).toHaveBeenCalledWith(mockDto); expect(result).toEqual([]); }); it('should propagate errors from API client', async () => { // Arrange const error = new Error('Failed to fetch sponsors'); vi.mocked(mockApiClient.getAll).mockRejectedValue(error); // Act & Assert await expect(service.getAllSponsors()).rejects.toThrow('Failed to fetch sponsors'); expect(mockApiClient.getAll).toHaveBeenCalled(); expect(mockSponsorListPresenter.present).not.toHaveBeenCalled(); }); }); describe('getSponsorDashboard', () => { it('should fetch sponsor dashboard and transform via presenter', async () => { // Arrange const sponsorId = 'sponsor-123'; const mockDto: SponsorDashboardDto = { sponsorId, sponsorName: 'Sponsor Alpha', totalSponsorships: 10, activeSponsorships: 7, totalInvestment: 50000, }; const mockViewModel: SponsorDashboardViewModel = { sponsorId, sponsorName: 'Sponsor Alpha', totalSponsorships: 10, activeSponsorships: 7, totalInvestment: 50000, } as SponsorDashboardViewModel; vi.mocked(mockApiClient.getDashboard).mockResolvedValue(mockDto); vi.mocked(mockSponsorDashboardPresenter.present).mockReturnValue(mockViewModel); // Act const result = await service.getSponsorDashboard(sponsorId); // Assert expect(mockApiClient.getDashboard).toHaveBeenCalledWith(sponsorId); expect(mockSponsorDashboardPresenter.present).toHaveBeenCalledWith(mockDto); expect(result).toEqual(mockViewModel); }); it('should return null when dashboard is not found', async () => { // Arrange const sponsorId = 'non-existent'; vi.mocked(mockApiClient.getDashboard).mockResolvedValue(null); // Act const result = await service.getSponsorDashboard(sponsorId); // Assert expect(mockApiClient.getDashboard).toHaveBeenCalledWith(sponsorId); expect(mockSponsorDashboardPresenter.present).not.toHaveBeenCalled(); expect(result).toBeNull(); }); it('should propagate errors from API client', async () => { // Arrange const sponsorId = 'sponsor-123'; const error = new Error('Failed to fetch dashboard'); vi.mocked(mockApiClient.getDashboard).mockRejectedValue(error); // Act & Assert await expect(service.getSponsorDashboard(sponsorId)).rejects.toThrow('Failed to fetch dashboard'); expect(mockApiClient.getDashboard).toHaveBeenCalledWith(sponsorId); expect(mockSponsorDashboardPresenter.present).not.toHaveBeenCalled(); }); }); describe('getSponsorSponsorships', () => { it('should fetch sponsor sponsorships and transform via presenter', async () => { // Arrange const sponsorId = 'sponsor-123'; const mockDto: SponsorSponsorshipsDto = { sponsorId, sponsorName: 'Sponsor Alpha', sponsorships: [ { id: 'sponsorship-1', leagueId: 'league-1', leagueName: 'League One', seasonId: 'season-1', tier: 'main', status: 'active', amount: 10000, currency: 'USD', }, ], }; const mockViewModel: SponsorSponsorshipsViewModel = { sponsorId, sponsorName: 'Sponsor Alpha', sponsorships: [], } as SponsorSponsorshipsViewModel; vi.mocked(mockApiClient.getSponsorships).mockResolvedValue(mockDto); vi.mocked(mockSponsorSponsorshipsPresenter.present).mockReturnValue(mockViewModel); // Act const result = await service.getSponsorSponsorships(sponsorId); // Assert expect(mockApiClient.getSponsorships).toHaveBeenCalledWith(sponsorId); expect(mockSponsorSponsorshipsPresenter.present).toHaveBeenCalledWith(mockDto); expect(result).toEqual(mockViewModel); }); it('should return null when sponsorships are not found', async () => { // Arrange const sponsorId = 'non-existent'; vi.mocked(mockApiClient.getSponsorships).mockResolvedValue(null); // Act const result = await service.getSponsorSponsorships(sponsorId); // Assert expect(mockApiClient.getSponsorships).toHaveBeenCalledWith(sponsorId); expect(mockSponsorSponsorshipsPresenter.present).not.toHaveBeenCalled(); expect(result).toBeNull(); }); it('should propagate errors from API client', async () => { // Arrange const sponsorId = 'sponsor-123'; const error = new Error('Failed to fetch sponsorships'); vi.mocked(mockApiClient.getSponsorships).mockRejectedValue(error); // Act & Assert await expect(service.getSponsorSponsorships(sponsorId)).rejects.toThrow('Failed to fetch sponsorships'); expect(mockApiClient.getSponsorships).toHaveBeenCalledWith(sponsorId); expect(mockSponsorSponsorshipsPresenter.present).not.toHaveBeenCalled(); }); }); describe('createSponsor', () => { it('should create a new sponsor', async () => { // Arrange const input: CreateSponsorInputDto = { name: 'New Sponsor', logoUrl: 'https://example.com/logo.png', websiteUrl: 'https://newsponsor.com', userId: 'user-123', }; const mockOutput: CreateSponsorOutputDto = { sponsorId: 'sponsor-new', success: true, }; vi.mocked(mockApiClient.create).mockResolvedValue(mockOutput); // Act const result = await service.createSponsor(input); // Assert expect(mockApiClient.create).toHaveBeenCalledWith(input); expect(result).toEqual(mockOutput); }); it('should propagate errors from API client', async () => { // Arrange const input: CreateSponsorInputDto = { name: 'New Sponsor', userId: 'user-123', }; const error = new Error('Failed to create sponsor'); vi.mocked(mockApiClient.create).mockRejectedValue(error); // Act & Assert await expect(service.createSponsor(input)).rejects.toThrow('Failed to create sponsor'); expect(mockApiClient.create).toHaveBeenCalledWith(input); }); }); describe('getSponsorshipPricing', () => { it('should fetch sponsorship pricing', async () => { // Arrange const mockPricing: GetEntitySponsorshipPricingResultDto = { pricingItems: [ { tier: 'main', price: 10000, currency: 'USD', benefits: ['Logo placement', 'Race announcements'], }, { tier: 'secondary', price: 5000, currency: 'USD', benefits: ['Logo placement'], }, ], }; vi.mocked(mockApiClient.getPricing).mockResolvedValue(mockPricing); // Act const result = await service.getSponsorshipPricing(); // Assert expect(mockApiClient.getPricing).toHaveBeenCalled(); expect(result).toEqual(mockPricing); }); it('should propagate errors from API client', async () => { // Arrange const error = new Error('Failed to fetch pricing'); vi.mocked(mockApiClient.getPricing).mockRejectedValue(error); // Act & Assert await expect(service.getSponsorshipPricing()).rejects.toThrow('Failed to fetch pricing'); expect(mockApiClient.getPricing).toHaveBeenCalled(); }); }); });