Files
gridpilot.gg/core/racing/application/use-cases/AcceptSponsorshipRequestUseCase.test.ts
2026-01-16 19:46:49 +01:00

251 lines
8.2 KiB
TypeScript

import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { WalletRepository } from '../../../payments/domain/repositories/WalletRepository';
import { LeagueWallet } from '../../domain/entities/league-wallet/LeagueWallet';
import { Season } from '../../domain/entities/season/Season';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
import { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import { Money } from '../../domain/value-objects/Money';
import { AcceptSponsorshipRequestUseCase } from './AcceptSponsorshipRequestUseCase';
describe('AcceptSponsorshipRequestUseCase', () => {
let mockSponsorshipRequestRepo: {
findById: Mock;
update: Mock;
};
let mockSeasonSponsorshipRepo: {
create: Mock;
};
let mockSeasonRepo: {
findById: Mock;
};
let mockNotificationService: {
sendNotification: Mock;
};
let processPayment: Mock;
let mockWalletRepo: {
findById: Mock;
update: Mock;
};
let mockLeagueWalletRepo: {
findById: Mock;
update: Mock;
};
let mockLogger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
beforeEach(() => {
mockSponsorshipRequestRepo = {
findById: vi.fn(),
update: vi.fn(),
};
mockSeasonSponsorshipRepo = {
create: vi.fn(),
};
mockSeasonRepo = {
findById: vi.fn(),
};
mockNotificationService = {
sendNotification: vi.fn(),
};
processPayment = vi.fn();
mockWalletRepo = {
findById: vi.fn(),
update: vi.fn(),
};
mockLeagueWalletRepo = {
findById: vi.fn(),
update: vi.fn(),
};
mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
});
it('should send notification to sponsor, process payment, update wallets, and return result when accepting season sponsorship', async () => {
const useCase = new AcceptSponsorshipRequestUseCase(
mockSponsorshipRequestRepo as unknown as SponsorshipRequestRepository,
mockSeasonSponsorshipRepo as unknown as SeasonSponsorshipRepository,
mockSeasonRepo as unknown as SeasonRepository,
mockNotificationService as unknown as NotificationService,
processPayment,
mockWalletRepo as unknown as WalletRepository,
mockLeagueWalletRepo as unknown as LeagueWalletRepository,
mockLogger as unknown as Logger,
);
const request = SponsorshipRequest.create({
id: 'req1',
sponsorId: 'sponsor1',
entityId: 'season1',
entityType: 'season',
tier: 'main',
offeredAmount: Money.create(1000),
status: 'pending',
});
const season = Season.create({
id: 'season1',
leagueId: 'league1',
gameId: 'game1',
name: 'Season 1',
startDate: new Date(),
endDate: new Date(),
});
mockSponsorshipRequestRepo.findById.mockResolvedValue(request);
mockSeasonRepo.findById.mockResolvedValue(season);
mockNotificationService.sendNotification.mockResolvedValue(undefined);
processPayment.mockResolvedValue({
success: true,
transactionId: 'txn1',
timestamp: new Date(),
});
mockWalletRepo.findById.mockResolvedValue({
id: 'sponsor1',
leagueId: 'league1',
balance: 2000,
totalRevenue: 0,
totalPlatformFees: 0,
totalWithdrawn: 0,
currency: 'USD',
createdAt: new Date(),
});
const leagueWallet = LeagueWallet.create({
id: 'league1',
leagueId: 'league1',
balance: Money.create(500),
});
mockLeagueWalletRepo.findById.mockResolvedValue(leagueWallet);
const result = await useCase.execute({
requestId: 'req1',
respondedBy: 'driver1',
});
expect(result.isOk()).toBe(true);
const successResult = result.unwrap();
expect(successResult.requestId).toBe('req1');
expect(successResult.status).toBe('accepted');
expect(successResult.sponsorshipId).toBeDefined();
expect(successResult.acceptedAt).toBeInstanceOf(Date);
expect(successResult.platformFee).toBeDefined();
expect(successResult.netAmount).toBeDefined();
expect(mockNotificationService.sendNotification).toHaveBeenCalledWith({
recipientId: 'sponsor1',
type: 'sponsorship_request_accepted',
title: 'Sponsorship Accepted',
body: 'Your sponsorship request for Season 1 has been accepted.',
channel: 'in_app',
urgency: 'toast',
data: {
requestId: 'req1',
sponsorshipId: expect.any(String),
},
});
expect(processPayment).toHaveBeenCalledWith({
amount: 1000,
payerId: 'sponsor1',
description: 'Sponsorship payment for season season1',
metadata: { requestId: 'req1' },
});
expect(mockWalletRepo.update).toHaveBeenCalledWith(
expect.objectContaining({
id: 'sponsor1',
balance: 1000,
}),
);
expect(mockLeagueWalletRepo.update).toHaveBeenCalledTimes(1);
const updatedLeagueWallet = (mockLeagueWalletRepo.update as Mock).mock.calls[0]?.[0] as LeagueWallet;
type ToStringable = { toString(): string };
const asString = (value: unknown): string => {
if (typeof value === 'string') return value;
if (
value &&
typeof value === 'object' &&
'toString' in value &&
typeof (value as ToStringable).toString === 'function'
) {
return (value as ToStringable).toString();
}
return String(value);
};
const updatedLeagueWalletId = (updatedLeagueWallet as unknown as { id: unknown }).id;
const updatedLeagueWalletBalanceAmount = (updatedLeagueWallet as unknown as { balance: { amount: number } })
.balance.amount;
expect(asString(updatedLeagueWalletId)).toBe('league1');
expect(updatedLeagueWalletBalanceAmount).toBe(1400);
});
it('should return error when sponsorship request not found', async () => {
const useCase = new AcceptSponsorshipRequestUseCase(
mockSponsorshipRequestRepo as unknown as SponsorshipRequestRepository,
mockSeasonSponsorshipRepo as unknown as SeasonSponsorshipRepository,
mockSeasonRepo as unknown as SeasonRepository,
mockNotificationService as unknown as NotificationService,
processPayment,
mockWalletRepo as unknown as WalletRepository,
mockLeagueWalletRepo as unknown as LeagueWalletRepository,
mockLogger as unknown as Logger,
);
mockSponsorshipRequestRepo.findById.mockResolvedValue(null);
const result = await useCase.execute({
requestId: 'req1',
respondedBy: 'driver1',
});
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('SPONSORSHIP_REQUEST_NOT_FOUND');
});
it('should return error when sponsorship request is not pending', async () => {
const useCase = new AcceptSponsorshipRequestUseCase(
mockSponsorshipRequestRepo as unknown as SponsorshipRequestRepository,
mockSeasonSponsorshipRepo as unknown as SeasonSponsorshipRepository,
mockSeasonRepo as unknown as SeasonRepository,
mockNotificationService as unknown as NotificationService,
processPayment,
mockWalletRepo as unknown as WalletRepository,
mockLeagueWalletRepo as unknown as LeagueWalletRepository,
mockLogger as unknown as Logger,
);
const request = SponsorshipRequest.create({
id: 'req1',
sponsorId: 'sponsor1',
entityId: 'season1',
entityType: 'season',
tier: 'main',
offeredAmount: Money.create(1000),
status: 'accepted',
});
mockSponsorshipRequestRepo.findById.mockResolvedValue(request);
const result = await useCase.execute({
requestId: 'req1',
respondedBy: 'driver1',
});
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('SPONSORSHIP_REQUEST_NOT_PENDING');
});
});