Copy // src/services/__tests__/order-service.integration.test.ts
import { v4 as uuidv4 } from 'uuid';
import { Database, createTestDatabase } from '../../config/database.config';
import { OrderService } from '../order-service';
import { OrderRepository } from '../../repositories/order-repository';
import { ProductRepository } from '../../repositories/product-repository';
import { InventoryService } from '../inventory-service';
import { InventoryRepository } from '../../repositories/inventory-repository';
describe('OrderService Integration Tests', () => {
let db: Database;
let orderService: OrderService;
let orderRepository: OrderRepository;
let productRepository: ProductRepository;
let inventoryService: InventoryService;
let inventoryRepository: InventoryRepository;
beforeAll(async () => {
db = createTestDatabase();
// Initialize repositories
orderRepository = new OrderRepository(db);
productRepository = new ProductRepository(db);
inventoryRepository = new InventoryRepository(db);
inventoryService = new InventoryService(inventoryRepository);
orderService = new OrderService(
orderRepository,
productRepository,
inventoryService
);
// Create tables
await db.query(`
CREATE TABLE IF NOT EXISTS products (
id VARCHAR(36) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price DECIMAL(10, 2) NOT NULL
)
`);
await db.query(`
CREATE TABLE IF NOT EXISTS inventory (
product_id VARCHAR(36) PRIMARY KEY,
available INTEGER NOT NULL,
reserved INTEGER NOT NULL
)
`);
await db.query(`
CREATE TABLE IF NOT EXISTS orders (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(36) NOT NULL,
total DECIMAL(10, 2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL
)
`);
});
afterAll(async () => {
await db.query('DROP TABLE IF EXISTS orders CASCADE');
await db.query('DROP TABLE IF EXISTS inventory CASCADE');
await db.query('DROP TABLE IF EXISTS products CASCADE');
await db.close();
});
beforeEach(async () => {
await db.query('TRUNCATE orders CASCADE');
await db.query('TRUNCATE inventory CASCADE');
await db.query('TRUNCATE products CASCADE');
});
describe('createOrder', () => {
it('should create order with valid items', async () => {
// Setup: Create products and inventory
const productId = uuidv4();
await db.query(
'INSERT INTO products (id, name, price) VALUES ($1, $2, $3)',
[productId, 'Test Product', 29.99]
);
await db.query(
'INSERT INTO inventory (product_id, available, reserved) VALUES ($1, $2, $3)',
[productId, 100, 0]
);
// Create order
const dto = {
userId: uuidv4(),
items: [
{
productId,
quantity: 2,
price: 29.99,
},
],
};
const order = await orderService.createOrder(dto);
expect(order.id).toBeDefined();
expect(order.userId).toBe(dto.userId);
expect(order.total).toBe(59.98);
expect(order.status).toBe('pending');
// Verify inventory was reserved
const inventory = await db.query(
'SELECT * FROM inventory WHERE product_id = $1',
[productId]
);
expect(inventory[0].reserved).toBe(2);
expect(inventory[0].available).toBe(98);
});
it('should throw error for non-existent product', async () => {
const dto = {
userId: uuidv4(),
items: [
{
productId: 'non-existent-id',
quantity: 1,
price: 10.00,
},
],
};
await expect(orderService.createOrder(dto)).rejects.toThrow(
'Product not found'
);
});
it('should throw error for insufficient inventory', async () => {
const productId = uuidv4();
await db.query(
'INSERT INTO products (id, name, price) VALUES ($1, $2, $3)',
[productId, 'Test Product', 29.99]
);
await db.query(
'INSERT INTO inventory (product_id, available, reserved) VALUES ($1, $2, $3)',
[productId, 5, 0]
);
const dto = {
userId: uuidv4(),
items: [
{
productId,
quantity: 10, // More than available
price: 29.99,
},
],
};
await expect(orderService.createOrder(dto)).rejects.toThrow(
'Insufficient inventory'
);
});
it('should throw error for price mismatch', async () => {
const productId = uuidv4();
await db.query(
'INSERT INTO products (id, name, price) VALUES ($1, $2, $3)',
[productId, 'Test Product', 29.99]
);
await db.query(
'INSERT INTO inventory (product_id, available, reserved) VALUES ($1, $2, $3)',
[productId, 100, 0]
);
const dto = {
userId: uuidv4(),
items: [
{
productId,
quantity: 1,
price: 19.99, // Wrong price
},
],
};
await expect(orderService.createOrder(dto)).rejects.toThrow(
'Price mismatch'
);
});
});
describe('confirmOrder', () => {
it('should confirm pending order and deduct inventory', async () => {
// Setup
const productId = uuidv4();
await db.query(
'INSERT INTO products (id, name, price) VALUES ($1, $2, $3)',
[productId, 'Test Product', 29.99]
);
await db.query(
'INSERT INTO inventory (product_id, available, reserved) VALUES ($1, $2, $3)',
[productId, 100, 0]
);
// Create order
const dto = {
userId: uuidv4(),
items: [{ productId, quantity: 2, price: 29.99 }],
};
const order = await orderService.createOrder(dto);
// Confirm order
const confirmed = await orderService.confirmOrder(order.id);
expect(confirmed.status).toBe('confirmed');
// Verify inventory was deducted
const inventory = await db.query(
'SELECT * FROM inventory WHERE product_id = $1',
[productId]
);
expect(inventory[0].available).toBe(98);
expect(inventory[0].reserved).toBe(0);
});
});
});