xpeditis2.0/apps/backend/src/domain/entities/rate-quote.entity.spec.ts
2025-10-20 12:30:08 +02:00

241 lines
7.2 KiB
TypeScript

/**
* RateQuote Entity Unit Tests
*/
import { RateQuote } from './rate-quote.entity';
describe('RateQuote Entity', () => {
const validProps = {
id: 'quote-1',
carrierId: 'carrier-1',
carrierName: 'Maersk',
carrierCode: 'MAERSK',
origin: {
code: 'NLRTM',
name: 'Rotterdam',
country: 'Netherlands',
},
destination: {
code: 'USNYC',
name: 'New York',
country: 'United States',
},
pricing: {
baseFreight: 1000,
surcharges: [
{ type: 'BAF', description: 'Bunker Adjustment Factor', amount: 100, currency: 'USD' },
],
totalAmount: 1100,
currency: 'USD',
},
containerType: '40HC',
mode: 'FCL' as const,
etd: new Date('2025-11-01'),
eta: new Date('2025-11-20'),
transitDays: 19,
route: [
{
portCode: 'NLRTM',
portName: 'Rotterdam',
departure: new Date('2025-11-01'),
},
{
portCode: 'USNYC',
portName: 'New York',
arrival: new Date('2025-11-20'),
},
],
availability: 50,
frequency: 'Weekly',
vesselType: 'Container Ship',
co2EmissionsKg: 2500,
};
describe('create', () => {
it('should create rate quote with valid props', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.id).toBe('quote-1');
expect(rateQuote.carrierName).toBe('Maersk');
expect(rateQuote.origin.code).toBe('NLRTM');
expect(rateQuote.destination.code).toBe('USNYC');
expect(rateQuote.pricing.totalAmount).toBe(1100);
});
it('should set validUntil to 15 minutes from now', () => {
const before = new Date();
const rateQuote = RateQuote.create(validProps);
const after = new Date();
const expectedValidUntil = new Date(before.getTime() + 15 * 60 * 1000);
const diff = Math.abs(rateQuote.validUntil.getTime() - expectedValidUntil.getTime());
// Allow 1 second tolerance for test execution time
expect(diff).toBeLessThan(1000);
});
it('should throw error for non-positive total price', () => {
expect(() =>
RateQuote.create({
...validProps,
pricing: { ...validProps.pricing, totalAmount: 0 },
})
).toThrow('Total price must be positive');
});
it('should throw error for non-positive base freight', () => {
expect(() =>
RateQuote.create({
...validProps,
pricing: { ...validProps.pricing, baseFreight: 0 },
})
).toThrow('Base freight must be positive');
});
it('should throw error if ETA is not after ETD', () => {
expect(() =>
RateQuote.create({
...validProps,
eta: new Date('2025-10-31'),
})
).toThrow('ETA must be after ETD');
});
it('should throw error for non-positive transit days', () => {
expect(() =>
RateQuote.create({
...validProps,
transitDays: 0,
})
).toThrow('Transit days must be positive');
});
it('should throw error for negative availability', () => {
expect(() =>
RateQuote.create({
...validProps,
availability: -1,
})
).toThrow('Availability cannot be negative');
});
it('should throw error if route has less than 2 segments', () => {
expect(() =>
RateQuote.create({
...validProps,
route: [{ portCode: 'NLRTM', portName: 'Rotterdam' }],
})
).toThrow('Route must have at least origin and destination');
});
});
describe('isValid', () => {
it('should return true for non-expired quote', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.isValid()).toBe(true);
});
it('should return false for expired quote', () => {
const expiredQuote = RateQuote.fromPersistence({
...validProps,
validUntil: new Date(Date.now() - 1000), // 1 second ago
createdAt: new Date(),
updatedAt: new Date(),
});
expect(expiredQuote.isValid()).toBe(false);
});
});
describe('isExpired', () => {
it('should return false for non-expired quote', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.isExpired()).toBe(false);
});
it('should return true for expired quote', () => {
const expiredQuote = RateQuote.fromPersistence({
...validProps,
validUntil: new Date(Date.now() - 1000),
createdAt: new Date(),
updatedAt: new Date(),
});
expect(expiredQuote.isExpired()).toBe(true);
});
});
describe('hasAvailability', () => {
it('should return true when availability > 0', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.hasAvailability()).toBe(true);
});
it('should return false when availability = 0', () => {
const rateQuote = RateQuote.create({ ...validProps, availability: 0 });
expect(rateQuote.hasAvailability()).toBe(false);
});
});
describe('getTotalSurcharges', () => {
it('should calculate total surcharges', () => {
const rateQuote = RateQuote.create({
...validProps,
pricing: {
baseFreight: 1000,
surcharges: [
{ type: 'BAF', description: 'BAF', amount: 100, currency: 'USD' },
{ type: 'CAF', description: 'CAF', amount: 50, currency: 'USD' },
],
totalAmount: 1150,
currency: 'USD',
},
});
expect(rateQuote.getTotalSurcharges()).toBe(150);
});
});
describe('getTransshipmentCount', () => {
it('should return 0 for direct route', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.getTransshipmentCount()).toBe(0);
});
it('should return correct count for route with transshipments', () => {
const rateQuote = RateQuote.create({
...validProps,
route: [
{ portCode: 'NLRTM', portName: 'Rotterdam' },
{ portCode: 'ESBCN', portName: 'Barcelona' },
{ portCode: 'USNYC', portName: 'New York' },
],
});
expect(rateQuote.getTransshipmentCount()).toBe(1);
});
});
describe('isDirectRoute', () => {
it('should return true for direct route', () => {
const rateQuote = RateQuote.create(validProps);
expect(rateQuote.isDirectRoute()).toBe(true);
});
it('should return false for route with transshipments', () => {
const rateQuote = RateQuote.create({
...validProps,
route: [
{ portCode: 'NLRTM', portName: 'Rotterdam' },
{ portCode: 'ESBCN', portName: 'Barcelona' },
{ portCode: 'USNYC', portName: 'New York' },
],
});
expect(rateQuote.isDirectRoute()).toBe(false);
});
});
describe('getPricePerDay', () => {
it('should calculate price per day', () => {
const rateQuote = RateQuote.create(validProps);
const pricePerDay = rateQuote.getPricePerDay();
expect(pricePerDay).toBeCloseTo(1100 / 19, 2);
});
});
});