import { describe, it, expect } from 'vitest'; import { Result } from '@/packages/shared/result/Result'; import { CheckoutConfirmation } from '@/packages/domain/value-objects/CheckoutConfirmation'; import { CheckoutPrice } from '@/packages/domain/value-objects/CheckoutPrice'; import { CheckoutState } from '@/packages/domain/value-objects/CheckoutState'; /** * Contract tests for ICheckoutConfirmationPort * * Any implementation must: * 1. Accept CheckoutConfirmationRequest with price, state, sessionMetadata, timeoutMs * 2. Return Result with decision: confirmed, cancelled, or timeout * 3. Handle timeout gracefully by returning timeout decision * 4. Validate request parameters before processing */ export interface CheckoutConfirmationRequest { price: CheckoutPrice; state: CheckoutState; sessionMetadata: { sessionName: string; trackId: string; carIds: string[]; }; timeoutMs: number; } export interface ICheckoutConfirmationPort { requestCheckoutConfirmation( request: CheckoutConfirmationRequest ): Promise>; } describe('ICheckoutConfirmationPort contract', () => { it('should define the required interface structure', () => { // This test verifies the port interface contract exists const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async (_request: CheckoutConfirmationRequest) => { return Result.ok(CheckoutConfirmation.create('confirmed')); }, }; expect(mockPort.requestCheckoutConfirmation).toBeDefined(); expect(typeof mockPort.requestCheckoutConfirmation).toBe('function'); }); it('should accept valid CheckoutConfirmationRequest', async () => { const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async (request: CheckoutConfirmationRequest) => { expect(request.price).toBeInstanceOf(CheckoutPrice); expect(request.state).toBeInstanceOf(CheckoutState); expect(request.sessionMetadata).toBeDefined(); expect(request.sessionMetadata.sessionName).toBeTruthy(); expect(request.sessionMetadata.trackId).toBeTruthy(); expect(Array.isArray(request.sessionMetadata.carIds)).toBe(true); expect(request.timeoutMs).toBeGreaterThan(0); return Result.ok(CheckoutConfirmation.create('confirmed')); }, }; const request: CheckoutConfirmationRequest = { price: CheckoutPrice.fromString('$10.00'), state: CheckoutState.ready(), sessionMetadata: { sessionName: 'Test Session', trackId: 'spa', carIds: ['car1', 'car2'], }, timeoutMs: 30000, }; const result = await mockPort.requestCheckoutConfirmation(request); expect(result.isOk()).toBe(true); }); it('should return Result with CheckoutConfirmation on success', async () => { const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async () => { return Result.ok(CheckoutConfirmation.create('confirmed')); }, }; const request: CheckoutConfirmationRequest = { price: CheckoutPrice.fromString('$10.00'), state: CheckoutState.ready(), sessionMetadata: { sessionName: 'Test Session', trackId: 'spa', carIds: ['car1'], }, timeoutMs: 30000, }; const result = await mockPort.requestCheckoutConfirmation(request); expect(result.isOk()).toBe(true); const confirmation = result.unwrap(); expect(confirmation).toBeInstanceOf(CheckoutConfirmation); expect(confirmation.isConfirmed()).toBe(true); }); it('should support cancelled decision', async () => { const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async () => { return Result.ok(CheckoutConfirmation.create('cancelled')); }, }; const request: CheckoutConfirmationRequest = { price: CheckoutPrice.fromString('$10.00'), state: CheckoutState.ready(), sessionMetadata: { sessionName: 'Test Session', trackId: 'spa', carIds: ['car1'], }, timeoutMs: 30000, }; const result = await mockPort.requestCheckoutConfirmation(request); expect(result.isOk()).toBe(true); const confirmation = result.unwrap(); expect(confirmation.isCancelled()).toBe(true); }); it('should support timeout decision', async () => { const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async () => { return Result.ok(CheckoutConfirmation.create('timeout')); }, }; const request: CheckoutConfirmationRequest = { price: CheckoutPrice.fromString('$10.00'), state: CheckoutState.ready(), sessionMetadata: { sessionName: 'Test Session', trackId: 'spa', carIds: ['car1'], }, timeoutMs: 1000, }; const result = await mockPort.requestCheckoutConfirmation(request); expect(result.isOk()).toBe(true); const confirmation = result.unwrap(); expect(confirmation.isTimeout()).toBe(true); }); it('should return error Result for invalid requests', async () => { const mockPort: ICheckoutConfirmationPort = { requestCheckoutConfirmation: async (request: CheckoutConfirmationRequest) => { if (request.timeoutMs <= 0) { return Result.err(new Error('Timeout must be positive')); } if (!request.sessionMetadata.sessionName) { return Result.err(new Error('Session name is required')); } return Result.ok(CheckoutConfirmation.create('confirmed')); }, }; const invalidRequest: CheckoutConfirmationRequest = { price: CheckoutPrice.fromString('$10.00'), state: CheckoutState.ready(), sessionMetadata: { sessionName: '', trackId: 'spa', carIds: ['car1'], }, timeoutMs: 30000, }; const result = await mockPort.requestCheckoutConfirmation(invalidRequest); expect(result.isErr()).toBe(true); expect(result.unwrapErr().message).toContain('Session name'); }); });