integration tests
Some checks failed
CI / lint-typecheck (pull_request) Failing after 4m51s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
Some checks failed
CI / lint-typecheck (pull_request) Failing after 4m51s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
This commit is contained in:
306
tests/integration/database/DatabaseTestContext.ts
Normal file
306
tests/integration/database/DatabaseTestContext.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
import { vi } from 'vitest';
|
||||
|
||||
// Mock data types that match what the use cases expect
|
||||
export interface DriverData {
|
||||
id: string;
|
||||
iracingId: string;
|
||||
name: string;
|
||||
country: string;
|
||||
bio?: string;
|
||||
joinedAt: Date;
|
||||
category?: string;
|
||||
}
|
||||
|
||||
export interface TeamData {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
category?: string;
|
||||
isRecruiting: boolean;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface TeamMembership {
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
role: 'owner' | 'manager' | 'driver';
|
||||
status: 'active' | 'pending' | 'none';
|
||||
joinedAt: Date;
|
||||
}
|
||||
|
||||
// Simple in-memory repositories for testing
|
||||
export class TestDriverRepository {
|
||||
private drivers = new Map<string, DriverData>();
|
||||
|
||||
async findById(id: string): Promise<DriverData | null> {
|
||||
return this.drivers.get(id) || null;
|
||||
}
|
||||
|
||||
async create(driver: DriverData): Promise<DriverData> {
|
||||
if (this.drivers.has(driver.id)) {
|
||||
throw new Error('Driver already exists');
|
||||
}
|
||||
this.drivers.set(driver.id, driver);
|
||||
return driver;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.drivers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export class TestTeamRepository {
|
||||
private teams = new Map<string, TeamData>();
|
||||
|
||||
async findById(id: string): Promise<TeamData | null> {
|
||||
return this.teams.get(id) || null;
|
||||
}
|
||||
|
||||
async create(team: TeamData): Promise<TeamData> {
|
||||
// Check for duplicate team name/tag
|
||||
const existingTeams = Array.from(this.teams.values());
|
||||
for (const existing of existingTeams) {
|
||||
if (existing.name === team.name && existing.tag === team.tag) {
|
||||
const error: any = new Error(`Team already exists: ${team.name} (${team.tag})`);
|
||||
error.code = 'DUPLICATE_TEAM';
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
this.teams.set(team.id, team);
|
||||
return team;
|
||||
}
|
||||
|
||||
async findAll(): Promise<TeamData[]> {
|
||||
return Array.from(this.teams.values());
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.teams.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export class TestTeamMembershipRepository {
|
||||
private memberships = new Map<string, TeamMembership[]>();
|
||||
|
||||
async getMembership(teamId: string, driverId: string): Promise<TeamMembership | null> {
|
||||
const teamMemberships = this.memberships.get(teamId) || [];
|
||||
return teamMemberships.find(m => m.driverId === driverId) || null;
|
||||
}
|
||||
|
||||
async getActiveMembershipForDriver(driverId: string): Promise<TeamMembership | null> {
|
||||
for (const teamMemberships of this.memberships.values()) {
|
||||
const active = teamMemberships.find(m => m.driverId === driverId && m.status === 'active');
|
||||
if (active) return active;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async saveMembership(membership: TeamMembership): Promise<TeamMembership> {
|
||||
const teamMemberships = this.memberships.get(membership.teamId) || [];
|
||||
const existingIndex = teamMemberships.findIndex(
|
||||
m => m.driverId === membership.driverId
|
||||
);
|
||||
|
||||
if (existingIndex >= 0) {
|
||||
// Check if already active
|
||||
const existing = teamMemberships[existingIndex];
|
||||
if (existing && existing.status === 'active') {
|
||||
const error: any = new Error('Already a member');
|
||||
error.code = 'ALREADY_MEMBER';
|
||||
throw error;
|
||||
}
|
||||
teamMemberships[existingIndex] = membership;
|
||||
} else {
|
||||
teamMemberships.push(membership);
|
||||
}
|
||||
|
||||
this.memberships.set(membership.teamId, teamMemberships);
|
||||
return membership;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.memberships.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Mock use case implementations
|
||||
export class CreateTeamUseCase {
|
||||
constructor(
|
||||
private driverRepository: TestDriverRepository,
|
||||
private teamRepository: TestTeamRepository,
|
||||
private membershipRepository: TestTeamMembershipRepository
|
||||
) {}
|
||||
|
||||
async execute(input: {
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
}): Promise<{ isOk: () => boolean; isErr: () => boolean; error?: any }> {
|
||||
try {
|
||||
// Check if driver exists
|
||||
const driver = await this.driverRepository.findById(input.ownerId);
|
||||
if (!driver) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'VALIDATION_ERROR', details: { message: 'Driver not found' } }
|
||||
};
|
||||
}
|
||||
|
||||
// Check if driver already belongs to a team
|
||||
const existingMembership = await this.membershipRepository.getActiveMembershipForDriver(input.ownerId);
|
||||
if (existingMembership) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'VALIDATION_ERROR', details: { message: 'Driver already belongs to a team' } }
|
||||
};
|
||||
}
|
||||
|
||||
const teamId = `team-${Date.now()}-${Math.random()}`;
|
||||
const team: TeamData = {
|
||||
id: teamId,
|
||||
name: input.name,
|
||||
tag: input.tag,
|
||||
description: input.description,
|
||||
ownerId: input.ownerId,
|
||||
leagues: input.leagues,
|
||||
isRecruiting: false,
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
await this.teamRepository.create(team);
|
||||
|
||||
// Create owner membership
|
||||
const membership: TeamMembership = {
|
||||
teamId: team.id,
|
||||
driverId: input.ownerId,
|
||||
role: 'owner',
|
||||
status: 'active',
|
||||
joinedAt: new Date(),
|
||||
};
|
||||
|
||||
await this.membershipRepository.saveMembership(membership);
|
||||
|
||||
return {
|
||||
isOk: () => true,
|
||||
isErr: () => false,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: error.code || 'REPOSITORY_ERROR', details: { message: error.message } }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class JoinTeamUseCase {
|
||||
constructor(
|
||||
private driverRepository: TestDriverRepository,
|
||||
private teamRepository: TestTeamRepository,
|
||||
private membershipRepository: TestTeamMembershipRepository
|
||||
) {}
|
||||
|
||||
async execute(input: {
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
}): Promise<{ isOk: () => boolean; isErr: () => boolean; error?: any }> {
|
||||
try {
|
||||
// Check if team exists
|
||||
const team = await this.teamRepository.findById(input.teamId);
|
||||
if (!team) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'TEAM_NOT_FOUND', details: { message: 'Team not found' } }
|
||||
};
|
||||
}
|
||||
|
||||
// Check if driver exists
|
||||
const driver = await this.driverRepository.findById(input.driverId);
|
||||
if (!driver) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'DRIVER_NOT_FOUND', details: { message: 'Driver not found' } }
|
||||
};
|
||||
}
|
||||
|
||||
// Check if driver already belongs to a team
|
||||
const existingActive = await this.membershipRepository.getActiveMembershipForDriver(input.driverId);
|
||||
if (existingActive) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'ALREADY_MEMBER', details: { message: 'Driver already belongs to a team' } }
|
||||
};
|
||||
}
|
||||
|
||||
// Check if already has membership (pending or active)
|
||||
const existingMembership = await this.membershipRepository.getMembership(input.teamId, input.driverId);
|
||||
if (existingMembership) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: 'ALREADY_MEMBER', details: { message: 'Already a member or have a pending request' } }
|
||||
};
|
||||
}
|
||||
|
||||
const membership: TeamMembership = {
|
||||
teamId: input.teamId,
|
||||
driverId: input.driverId,
|
||||
role: 'driver',
|
||||
status: 'active',
|
||||
joinedAt: new Date(),
|
||||
};
|
||||
|
||||
await this.membershipRepository.saveMembership(membership);
|
||||
|
||||
return {
|
||||
isOk: () => true,
|
||||
isErr: () => false,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
isOk: () => false,
|
||||
isErr: () => true,
|
||||
error: { code: error.code || 'REPOSITORY_ERROR', details: { message: error.message } }
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class DatabaseTestContext {
|
||||
public readonly driverRepository: TestDriverRepository;
|
||||
public readonly teamRepository: TestTeamRepository;
|
||||
public readonly teamMembershipRepository: TestTeamMembershipRepository;
|
||||
public readonly createTeamUseCase: CreateTeamUseCase;
|
||||
public readonly joinTeamUseCase: JoinTeamUseCase;
|
||||
|
||||
constructor() {
|
||||
this.driverRepository = new TestDriverRepository();
|
||||
this.teamRepository = new TestTeamRepository();
|
||||
this.teamMembershipRepository = new TestTeamMembershipRepository();
|
||||
|
||||
this.createTeamUseCase = new CreateTeamUseCase(this.driverRepository, this.teamRepository, this.teamMembershipRepository);
|
||||
this.joinTeamUseCase = new JoinTeamUseCase(this.driverRepository, this.teamRepository, this.teamMembershipRepository);
|
||||
}
|
||||
|
||||
public clear(): void {
|
||||
this.driverRepository.clear();
|
||||
this.teamRepository.clear();
|
||||
this.teamMembershipRepository.clear();
|
||||
vi.clearAllMocks();
|
||||
}
|
||||
|
||||
public static create(): DatabaseTestContext {
|
||||
return new DatabaseTestContext();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user