251 lines
8.2 KiB
TypeScript
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');
|
|
});
|
|
});
|