/** * Integration Test: Team Admin Use Case Orchestration * * Tests the orchestration logic of team admin-related Use Cases: * - UpdateTeamUseCase: Admin updates team details * - Validates that Use Cases correctly interact with their Ports (Repositories) * - Uses In-Memory adapters for fast, deterministic testing * * Focus: Business logic orchestration, NOT UI rendering */ import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import { InMemoryTeamRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryTeamRepository'; import { InMemoryTeamMembershipRepository } from '../../../adapters/racing/persistence/inmemory/InMemoryTeamMembershipRepository'; import { UpdateTeamUseCase } from '../../../core/racing/application/use-cases/UpdateTeamUseCase'; import { Team } from '../../../core/racing/domain/entities/Team'; import { Logger } from '../../../core/shared/domain/Logger'; describe('Team Admin Use Case Orchestration', () => { let teamRepository: InMemoryTeamRepository; let membershipRepository: InMemoryTeamMembershipRepository; let updateTeamUseCase: UpdateTeamUseCase; let mockLogger: Logger; beforeAll(() => { mockLogger = { info: () => {}, debug: () => {}, warn: () => {}, error: () => {}, } as unknown as Logger; teamRepository = new InMemoryTeamRepository(mockLogger); membershipRepository = new InMemoryTeamMembershipRepository(mockLogger); updateTeamUseCase = new UpdateTeamUseCase(teamRepository, membershipRepository); }); beforeEach(() => { teamRepository.clear(); membershipRepository.clear(); }); describe('UpdateTeamUseCase - Success Path', () => { it('should update team details when called by owner', async () => { // Scenario: Owner updates team details // Given: A team exists const teamId = 't1'; const ownerId = 'o1'; const team = Team.create({ id: teamId, name: 'Old Name', tag: 'OLD', description: 'Old Desc', ownerId, leagues: [] }); await teamRepository.create(team); // And: The driver is the owner await membershipRepository.saveMembership({ teamId, driverId: ownerId, role: 'owner', status: 'active', joinedAt: new Date() }); // When: UpdateTeamUseCase.execute() is called const result = await updateTeamUseCase.execute({ teamId, updatedBy: ownerId, updates: { name: 'New Name', tag: 'NEW', description: 'New Desc' } }); // Then: The team should be updated successfully expect(result.isOk()).toBe(true); const { team: updatedTeam } = result.unwrap(); expect(updatedTeam.name.toString()).toBe('New Name'); expect(updatedTeam.tag.toString()).toBe('NEW'); expect(updatedTeam.description.toString()).toBe('New Desc'); // And: The changes should be in the repository const savedTeam = await teamRepository.findById(teamId); expect(savedTeam?.name.toString()).toBe('New Name'); }); it('should update team details when called by manager', async () => { // Scenario: Manager updates team details // Given: A team exists const teamId = 't2'; const managerId = 'm2'; const team = Team.create({ id: teamId, name: 'Team 2', tag: 'T2', description: 'Desc', ownerId: 'owner', leagues: [] }); await teamRepository.create(team); // And: The driver is a manager await membershipRepository.saveMembership({ teamId, driverId: managerId, role: 'manager', status: 'active', joinedAt: new Date() }); // When: UpdateTeamUseCase.execute() is called const result = await updateTeamUseCase.execute({ teamId, updatedBy: managerId, updates: { name: 'Updated by Manager' } }); // Then: The team should be updated successfully expect(result.isOk()).toBe(true); const { team: updatedTeam } = result.unwrap(); expect(updatedTeam.name.toString()).toBe('Updated by Manager'); }); }); describe('UpdateTeamUseCase - Validation', () => { it('should reject update when called by regular member', async () => { // Scenario: Regular member tries to update team // Given: A team exists const teamId = 't3'; const memberId = 'd3'; const team = Team.create({ id: teamId, name: 'Team 3', tag: 'T3', description: 'Desc', ownerId: 'owner', leagues: [] }); await teamRepository.create(team); // And: The driver is a regular member await membershipRepository.saveMembership({ teamId, driverId: memberId, role: 'driver', status: 'active', joinedAt: new Date() }); // When: UpdateTeamUseCase.execute() is called const result = await updateTeamUseCase.execute({ teamId, updatedBy: memberId, updates: { name: 'Unauthorized Update' } }); // Then: Should return error expect(result.isErr()).toBe(true); const error = result.unwrapErr(); expect(error.code).toBe('PERMISSION_DENIED'); }); it('should reject update when called by non-member', async () => { // Scenario: Non-member tries to update team // Given: A team exists const teamId = 't4'; const team = Team.create({ id: teamId, name: 'Team 4', tag: 'T4', description: 'Desc', ownerId: 'owner', leagues: [] }); await teamRepository.create(team); // When: UpdateTeamUseCase.execute() is called const result = await updateTeamUseCase.execute({ teamId, updatedBy: 'non-member', updates: { name: 'Unauthorized Update' } }); // Then: Should return error expect(result.isErr()).toBe(true); const error = result.unwrapErr(); expect(error.code).toBe('PERMISSION_DENIED'); }); }); describe('UpdateTeamUseCase - Error Handling', () => { it('should throw error when team does not exist', async () => { // Scenario: Non-existent team // Given: A driver exists who is a manager of some team const managerId = 'm5'; await membershipRepository.saveMembership({ teamId: 'some-team', driverId: managerId, role: 'manager', status: 'active', joinedAt: new Date() }); // When: UpdateTeamUseCase.execute() is called with non-existent team ID const result = await updateTeamUseCase.execute({ teamId: 'nonexistent', updatedBy: managerId, updates: { name: 'New Name' } }); // Then: Should return error expect(result.isErr()).toBe(true); const error = result.unwrapErr(); expect(error.code).toBe('PERMISSION_DENIED'); // Because membership check fails first }); }); });