Files
gridpilot.gg/tests/application/ICheckoutConfirmationPort.spec.ts
2025-12-16 11:52:26 +01:00

180 lines
6.0 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import { Result } from '@core/shared/result/Result';
import { CheckoutConfirmation } from '@core/automation/domain/value-objects/CheckoutConfirmation';
import { CheckoutPrice } from '@core/automation/domain/value-objects/CheckoutPrice';
import { CheckoutState } from '@core/automation/domain/value-objects/CheckoutState';
/**
* Contract tests for ICheckoutConfirmationPort
*
* Any implementation must:
* 1. Accept CheckoutConfirmationRequest with price, state, sessionMetadata, timeoutMs
* 2. Return Result<CheckoutConfirmation> 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<Result<CheckoutConfirmation>>;
}
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');
});
});