refactor use cases

This commit is contained in:
2026-01-08 15:34:51 +01:00
parent d984ab24a8
commit 52e9a2f6a7
362 changed files with 5192 additions and 8409 deletions

View File

@@ -3,7 +3,7 @@ import type { AcceptSponsorshipRequestUseCase } from '@core/racing/application/u
import type { CreateSponsorUseCase } from '@core/racing/application/use-cases/CreateSponsorUseCase';
import type { GetPendingSponsorshipRequestsUseCase } from '@core/racing/application/use-cases/GetPendingSponsorshipRequestsUseCase';
import type { GetSponsorDashboardInput, GetSponsorDashboardUseCase } from '@core/racing/application/use-cases/GetSponsorDashboardUseCase';
import type { GetSponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetSponsorshipPricingUseCase';
import type { GetEntitySponsorshipPricingUseCase } from '@core/racing/application/use-cases/GetEntitySponsorshipPricingUseCase';
import type { GetSponsorSponsorshipsInput, GetSponsorSponsorshipsUseCase } from '@core/racing/application/use-cases/GetSponsorSponsorshipsUseCase';
import type { GetSponsorsUseCase } from '@core/racing/application/use-cases/GetSponsorsUseCase';
import type { GetSponsorUseCase } from '@core/racing/application/use-cases/GetSponsorUseCase';
@@ -14,21 +14,11 @@ import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import type { CreateSponsorInputDTO } from './dtos/CreateSponsorInputDTO';
import { Sponsor } from '@core/racing/domain/entities/sponsor/Sponsor';
import { Money } from '@core/racing/domain/value-objects/Money';
import { AcceptSponsorshipRequestPresenter } from './presenters/AcceptSponsorshipRequestPresenter';
import { CreateSponsorPresenter } from './presenters/CreateSponsorPresenter';
import { GetEntitySponsorshipPricingPresenter } from './presenters/GetEntitySponsorshipPricingPresenter';
import { GetPendingSponsorshipRequestsPresenter } from './presenters/GetPendingSponsorshipRequestsPresenter';
import { GetSponsorDashboardPresenter } from './presenters/GetSponsorDashboardPresenter';
import { GetSponsorPresenter } from './presenters/GetSponsorPresenter';
import { GetSponsorSponsorshipsPresenter } from './presenters/GetSponsorSponsorshipsPresenter';
import { GetSponsorsPresenter } from './presenters/GetSponsorsPresenter';
import { RejectSponsorshipRequestPresenter } from './presenters/RejectSponsorshipRequestPresenter';
import { SponsorBillingPresenter } from './presenters/SponsorBillingPresenter';
import { SponsorService } from './SponsorService';
describe('SponsorService', () => {
let service: SponsorService;
let getSponsorshipPricingUseCase: { execute: Mock };
let getEntitySponsorshipPricingUseCase: { execute: Mock };
let getSponsorsUseCase: { execute: Mock };
let createSponsorUseCase: { execute: Mock };
let getSponsorDashboardUseCase: { execute: Mock };
@@ -40,20 +30,8 @@ describe('SponsorService', () => {
let getSponsorBillingUseCase: { execute: Mock };
let logger: Logger;
// Presenters
let getEntitySponsorshipPricingPresenter: GetEntitySponsorshipPricingPresenter;
let getSponsorsPresenter: GetSponsorsPresenter;
let createSponsorPresenter: CreateSponsorPresenter;
let getSponsorDashboardPresenter: GetSponsorDashboardPresenter;
let getSponsorSponsorshipsPresenter: GetSponsorSponsorshipsPresenter;
let getSponsorPresenter: GetSponsorPresenter;
let getPendingSponsorshipRequestsPresenter: GetPendingSponsorshipRequestsPresenter;
let acceptSponsorshipRequestPresenter: AcceptSponsorshipRequestPresenter;
let rejectSponsorshipRequestPresenter: RejectSponsorshipRequestPresenter;
let sponsorBillingPresenter: SponsorBillingPresenter;
beforeEach(() => {
getSponsorshipPricingUseCase = { execute: vi.fn() };
getEntitySponsorshipPricingUseCase = { execute: vi.fn() };
getSponsorsUseCase = { execute: vi.fn() };
createSponsorUseCase = { execute: vi.fn() };
getSponsorDashboardUseCase = { execute: vi.fn() };
@@ -70,20 +48,8 @@ describe('SponsorService', () => {
error: vi.fn(),
} as unknown as Logger;
// Initialize presenters
getEntitySponsorshipPricingPresenter = new GetEntitySponsorshipPricingPresenter();
getSponsorsPresenter = new GetSponsorsPresenter();
createSponsorPresenter = new CreateSponsorPresenter();
getSponsorDashboardPresenter = new GetSponsorDashboardPresenter();
getSponsorSponsorshipsPresenter = new GetSponsorSponsorshipsPresenter();
getSponsorPresenter = new GetSponsorPresenter();
getPendingSponsorshipRequestsPresenter = new GetPendingSponsorshipRequestsPresenter();
acceptSponsorshipRequestPresenter = new AcceptSponsorshipRequestPresenter();
rejectSponsorshipRequestPresenter = new RejectSponsorshipRequestPresenter();
sponsorBillingPresenter = new SponsorBillingPresenter();
service = new SponsorService(
getSponsorshipPricingUseCase as unknown as GetSponsorshipPricingUseCase,
getEntitySponsorshipPricingUseCase as unknown as GetEntitySponsorshipPricingUseCase,
getSponsorsUseCase as unknown as GetSponsorsUseCase,
createSponsorUseCase as unknown as CreateSponsorUseCase,
getSponsorDashboardUseCase as unknown as GetSponsorDashboardUseCase,
@@ -94,31 +60,19 @@ describe('SponsorService', () => {
rejectSponsorshipRequestUseCase as unknown as RejectSponsorshipRequestUseCase,
getSponsorBillingUseCase as unknown as GetSponsorBillingUseCase,
logger,
getEntitySponsorshipPricingPresenter,
getSponsorsPresenter,
createSponsorPresenter,
getSponsorDashboardPresenter,
getSponsorSponsorshipsPresenter,
getSponsorPresenter,
getPendingSponsorshipRequestsPresenter,
acceptSponsorshipRequestPresenter,
rejectSponsorshipRequestPresenter,
sponsorBillingPresenter,
);
});
describe('getEntitySponsorshipPricing', () => {
it('returns pricing data on success', async () => {
const outputPort = {
const output = {
entityType: 'season',
entityId: 'season-1',
acceptingApplications: true,
tiers: [{ name: 'Gold', price: { amount: 500, currency: 'USD' }, benefits: ['Main slot'] }],
};
getSponsorshipPricingUseCase.execute.mockImplementation(async () => {
getEntitySponsorshipPricingPresenter.present(outputPort as any);
return Result.ok(undefined);
});
getEntitySponsorshipPricingUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.getEntitySponsorshipPricing();
@@ -130,7 +84,7 @@ describe('SponsorService', () => {
});
it('returns empty pricing on error', async () => {
getSponsorshipPricingUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
getEntitySponsorshipPricingUseCase.execute.mockResolvedValue(Result.err({ code: 'REPOSITORY_ERROR' }));
const result = await service.getEntitySponsorshipPricing();
@@ -153,10 +107,7 @@ describe('SponsorService', () => {
}),
];
getSponsorsUseCase.execute.mockImplementation(async () => {
getSponsorsPresenter.present(sponsors);
return Result.ok(undefined);
});
getSponsorsUseCase.execute.mockResolvedValue(Result.ok({ sponsors }));
const result = await service.getSponsors();
@@ -191,10 +142,7 @@ describe('SponsorService', () => {
createdAt: new Date('2024-01-01T00:00:00Z'),
});
createSponsorUseCase.execute.mockImplementation(async () => {
createSponsorPresenter.present(sponsor);
return Result.ok(undefined);
});
createSponsorUseCase.execute.mockResolvedValue(Result.ok({ sponsor }));
const result = await service.createSponsor(input);
@@ -235,7 +183,7 @@ describe('SponsorService', () => {
describe('getSponsorDashboard', () => {
it('returns dashboard on success', async () => {
const params: GetSponsorDashboardInput = { sponsorId: 's1' };
const outputPort = {
const output = {
sponsorId: 's1',
sponsorName: 'S1',
metrics: {
@@ -254,19 +202,25 @@ describe('SponsorService', () => {
totalInvestment: Money.create(0, 'USD'),
costPerThousandViews: 0,
},
sponsorships: {
leagues: [],
teams: [],
drivers: [],
races: [],
platform: [],
},
recentActivity: [],
upcomingRenewals: [],
};
getSponsorDashboardUseCase.execute.mockImplementation(async () => {
getSponsorDashboardPresenter.present(outputPort as any);
return Result.ok(undefined);
});
getSponsorDashboardUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.getSponsorDashboard(params);
expect(result).toEqual({
sponsorId: 's1',
sponsorName: 'S1',
metrics: outputPort.metrics,
metrics: output.metrics,
sponsoredLeagues: [],
investment: {
activeSponsorships: 0,
@@ -296,7 +250,7 @@ describe('SponsorService', () => {
describe('getSponsorSponsorships', () => {
it('returns sponsorships on success', async () => {
const params: GetSponsorSponsorshipsInput = { sponsorId: 's1' };
const outputPort = {
const output = {
sponsor: Sponsor.create({
id: 's1',
name: 'S1',
@@ -311,10 +265,7 @@ describe('SponsorService', () => {
},
};
getSponsorSponsorshipsUseCase.execute.mockImplementation(async () => {
getSponsorSponsorshipsPresenter.present(outputPort as any);
return Result.ok(undefined);
});
getSponsorSponsorshipsUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.getSponsorSponsorships(params);
@@ -345,16 +296,25 @@ describe('SponsorService', () => {
describe('getSponsor', () => {
it('returns sponsor when found', async () => {
const sponsorId = 's1';
const output = { sponsor: { id: sponsorId, name: 'S1' } };
getSponsorUseCase.execute.mockImplementation(async () => {
getSponsorPresenter.present(output);
return Result.ok(undefined);
const sponsor = Sponsor.create({
id: sponsorId,
name: 'S1',
contactEmail: 's1@example.com',
createdAt: new Date('2024-01-01T00:00:00Z'),
});
getSponsorUseCase.execute.mockResolvedValue(Result.ok({ sponsor }));
const result = await service.getSponsor(sponsorId);
expect(result).toEqual(output);
expect(result).toEqual({
sponsor: {
id: sponsorId,
name: 'S1',
logoUrl: undefined,
websiteUrl: undefined,
},
});
});
it('throws when not found', async () => {
@@ -375,21 +335,18 @@ describe('SponsorService', () => {
describe('getPendingSponsorshipRequests', () => {
it('returns requests on success', async () => {
const params = { entityType: 'season' as const, entityId: 'season-1' };
const outputPort = {
const output = {
entityType: 'season',
entityId: 'season-1',
requests: [],
totalCount: 0,
};
getPendingSponsorshipRequestsUseCase.execute.mockImplementation(async () => {
getPendingSponsorshipRequestsPresenter.present(outputPort as any);
return Result.ok(undefined);
});
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.getPendingSponsorshipRequests(params);
expect(result).toEqual(outputPort);
expect(result).toEqual(output);
});
it('returns empty result on error', async () => {
@@ -405,20 +362,13 @@ describe('SponsorService', () => {
totalCount: 0,
});
});
it('throws when presenter viewModel is missing on success', async () => {
const params = { entityType: 'season' as const, entityId: 'season-1' };
getPendingSponsorshipRequestsUseCase.execute.mockResolvedValue(Result.ok(undefined));
await expect(service.getPendingSponsorshipRequests(params)).rejects.toThrow('Pending sponsorship requests not found');
});
});
describe('SponsorshipRequest', () => {
it('returns accept result on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const outputPort = {
const output = {
requestId,
sponsorshipId: 'sp1',
status: 'accepted' as const,
@@ -427,14 +377,11 @@ describe('SponsorService', () => {
netAmount: 90,
};
acceptSponsorshipRequestUseCase.execute.mockImplementation(async () => {
acceptSponsorshipRequestPresenter.present(outputPort as any);
return Result.ok(undefined);
});
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.acceptSponsorshipRequest(requestId, respondedBy);
expect(result).toEqual(outputPort);
expect(result).toEqual(output);
});
it('throws on error', async () => {
@@ -448,16 +395,6 @@ describe('SponsorService', () => {
'Accept sponsorship request failed',
);
});
it('throws when presenter viewModel is missing on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
acceptSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(undefined));
await expect(service.acceptSponsorshipRequest(requestId, respondedBy)).rejects.toThrow(
'Accept sponsorship request failed',
);
});
});
describe('rejectSponsorshipRequest', () => {
@@ -472,10 +409,7 @@ describe('SponsorService', () => {
rejectionReason: reason,
};
rejectSponsorshipRequestUseCase.execute.mockImplementation(async () => {
rejectSponsorshipRequestPresenter.present(output as any);
return Result.ok(undefined);
});
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
const result = await service.rejectSponsorshipRequest(requestId, respondedBy, reason);
@@ -485,19 +419,18 @@ describe('SponsorService', () => {
it('passes no reason when reason is undefined', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
const output = {
requestId,
status: 'rejected' as const,
respondedAt: new Date(),
rejectionReason: '',
};
rejectSponsorshipRequestUseCase.execute.mockImplementation(async (input: any) => {
expect(input).toEqual({ requestId, respondedBy });
rejectSponsorshipRequestPresenter.present({
requestId,
status: 'rejected' as const,
respondedAt: new Date(),
rejectionReason: '',
} as any);
return Result.ok(undefined);
});
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(output));
await expect(service.rejectSponsorshipRequest(requestId, respondedBy)).resolves.toMatchObject({
const result = await service.rejectSponsorshipRequest(requestId, respondedBy);
expect(result).toMatchObject({
requestId,
status: 'rejected',
});
@@ -514,16 +447,6 @@ describe('SponsorService', () => {
'Reject sponsorship request failed',
);
});
it('throws when presenter viewModel is missing on success', async () => {
const requestId = 'r1';
const respondedBy = 'u1';
rejectSponsorshipRequestUseCase.execute.mockResolvedValue(Result.ok(undefined));
await expect(service.rejectSponsorshipRequest(requestId, respondedBy)).rejects.toThrow(
'Reject sponsorship request failed',
);
});
});
describe('getSponsorBilling', () => {