wip
This commit is contained in:
180
tests/unit/application/ports/ICheckoutConfirmationPort.test.ts
Normal file
180
tests/unit/application/ports/ICheckoutConfirmationPort.test.ts
Normal file
@@ -0,0 +1,180 @@
|
||||
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<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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user