diff --git a/api/src/repositories/headquartersRepo.ts b/api/src/repositories/headquartersRepo.ts index 112dbdc..6f5b145 100644 --- a/api/src/repositories/headquartersRepo.ts +++ b/api/src/repositories/headquartersRepo.ts @@ -145,8 +145,12 @@ let headquartersRepo: HeadquartersRepository | null = null; export async function getHeadquartersRepository( isTest: boolean = false, ): Promise { + const isTestEnv = isTest || process.env.NODE_ENV === 'test' || process.env.VITEST === 'true'; + if (isTestEnv) { + return createHeadquartersRepository(true); + } if (!headquartersRepo) { - headquartersRepo = await createHeadquartersRepository(isTest); + headquartersRepo = await createHeadquartersRepository(false); } return headquartersRepo; } diff --git a/api/src/repositories/orderDetailDeliveriesRepo.ts b/api/src/repositories/orderDetailDeliveriesRepo.ts index eec2eac..91ee239 100644 --- a/api/src/repositories/orderDetailDeliveriesRepo.ts +++ b/api/src/repositories/orderDetailDeliveriesRepo.ts @@ -187,8 +187,12 @@ let orderDetailDeliveriesRepo: OrderDetailDeliveriesRepository | null = null; export async function getOrderDetailDeliveriesRepository( isTest: boolean = false, ): Promise { + const isTestEnv = isTest || process.env.NODE_ENV === 'test' || process.env.VITEST === 'true'; + if (isTestEnv) { + return createOrderDetailDeliveriesRepository(true); + } if (!orderDetailDeliveriesRepo) { - orderDetailDeliveriesRepo = await createOrderDetailDeliveriesRepository(isTest); + orderDetailDeliveriesRepo = await createOrderDetailDeliveriesRepository(false); } return orderDetailDeliveriesRepo; } diff --git a/api/src/repositories/orderDetailsRepo.ts b/api/src/repositories/orderDetailsRepo.ts index 5e1c0dc..9c17a77 100644 --- a/api/src/repositories/orderDetailsRepo.ts +++ b/api/src/repositories/orderDetailsRepo.ts @@ -175,8 +175,12 @@ let orderDetailsRepo: OrderDetailsRepository | null = null; export async function getOrderDetailsRepository( isTest: boolean = false, ): Promise { + const isTestEnv = isTest || process.env.NODE_ENV === 'test' || process.env.VITEST === 'true'; + if (isTestEnv) { + return createOrderDetailsRepository(true); + } if (!orderDetailsRepo) { - orderDetailsRepo = await createOrderDetailsRepository(isTest); + orderDetailsRepo = await createOrderDetailsRepository(false); } return orderDetailsRepo; } diff --git a/api/src/repositories/productsRepo.ts b/api/src/repositories/productsRepo.ts index def3f4f..d405fca 100644 --- a/api/src/repositories/productsRepo.ts +++ b/api/src/repositories/productsRepo.ts @@ -152,8 +152,12 @@ export async function createProductsRepository( let productsRepo: ProductsRepository | null = null; export async function getProductsRepository(isTest: boolean = false): Promise { + const isTestEnv = isTest || process.env.NODE_ENV === 'test' || process.env.VITEST === 'true'; + if (isTestEnv) { + return createProductsRepository(true); + } if (!productsRepo) { - productsRepo = await createProductsRepository(isTest); + productsRepo = await createProductsRepository(false); } return productsRepo; } diff --git a/api/src/routes/delivery.test.ts b/api/src/routes/delivery.test.ts new file mode 100644 index 0000000..46ad175 --- /dev/null +++ b/api/src/routes/delivery.test.ts @@ -0,0 +1,141 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import deliveryRouter from './delivery'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Delivery API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Seed required foreign key: supplier + const db = await getDatabase(); + await db.run( + 'INSERT INTO suppliers (supplier_id, name, description, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?)', + [1, 'Test Supplier', 'Test supplier desc', 'Supplier Contact', 'supplier@test.com', '555-0000'], + ); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/deliveries', deliveryRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new delivery', async () => { + const newDelivery = { + supplierId: 1, + deliveryDate: '2024-02-01T14:00:00Z', + name: 'Test Delivery', + description: 'Test delivery description', + status: 'pending', + }; + const response = await request(app).post('/deliveries').send(newDelivery); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newDelivery); + expect(response.body.deliveryId).toBeDefined(); + }); + + it('should get all deliveries', async () => { + const response = await request(app).get('/deliveries'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get a delivery by ID', async () => { + // First create a delivery to test getting it + const newDelivery = { + supplierId: 1, + deliveryDate: '2024-02-02T14:00:00Z', + name: 'Delivery To Get', + description: 'Test delivery', + status: 'in-transit', + }; + const createResponse = await request(app).post('/deliveries').send(newDelivery); + const deliveryId = createResponse.body.deliveryId; + + const response = await request(app).get(`/deliveries/${deliveryId}`); + expect(response.status).toBe(200); + expect(response.body.deliveryId).toBe(deliveryId); + }); + + it('should update a delivery by ID', async () => { + // First create a delivery to test updating it + const newDelivery = { + supplierId: 1, + deliveryDate: '2024-02-03T14:00:00Z', + name: 'Original Delivery', + description: 'Original description', + status: 'pending', + }; + const createResponse = await request(app).post('/deliveries').send(newDelivery); + const deliveryId = createResponse.body.deliveryId; + + const updatedDelivery = { + ...newDelivery, + status: 'delivered', + description: 'Updated description', + }; + const response = await request(app).put(`/deliveries/${deliveryId}`).send(updatedDelivery); + expect(response.status).toBe(200); + expect(response.body.status).toBe('delivered'); + expect(response.body.description).toBe('Updated description'); + }); + + it('should update delivery status', async () => { + // First create a delivery to test updating status + const newDelivery = { + supplierId: 1, + deliveryDate: '2024-02-04T14:00:00Z', + name: 'Delivery Status Update', + description: 'Test delivery status update', + status: 'pending', + }; + const createResponse = await request(app).post('/deliveries').send(newDelivery); + const deliveryId = createResponse.body.deliveryId; + + const response = await request(app) + .put(`/deliveries/${deliveryId}/status`) + .send({ status: 'delivered' }); + expect(response.status).toBe(200); + expect(response.body.status).toBe('delivered'); + }); + + it('should delete a delivery by ID', async () => { + // First create a delivery to test deleting it + const newDelivery = { + supplierId: 1, + deliveryDate: '2024-02-05T14:00:00Z', + name: 'Delivery To Delete', + description: 'This delivery will be deleted', + status: 'failed', + }; + const createResponse = await request(app).post('/deliveries').send(newDelivery); + const deliveryId = createResponse.body.deliveryId; + + const response = await request(app).delete(`/deliveries/${deliveryId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing delivery', async () => { + const response = await request(app).get('/deliveries/999'); + expect(response.status).toBe(404); + }); + + it('should return 404 when updating status of non-existing delivery', async () => { + const response = await request(app).put('/deliveries/999/status').send({ status: 'delivered' }); + expect(response.status).toBe(404); + }); +}); diff --git a/api/src/routes/headquarters.test.ts b/api/src/routes/headquarters.test.ts new file mode 100644 index 0000000..e00eb57 --- /dev/null +++ b/api/src/routes/headquarters.test.ts @@ -0,0 +1,114 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import headquartersRouter from './headquarters'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Headquarters API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/headquarters', headquartersRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new headquarters', async () => { + const newHeadquarters = { + name: 'Main Headquarters', + description: 'Central office location', + address: '100 Main Street', + contactPerson: 'Alice Johnson', + email: 'alice@hq.com', + phone: '555-1000', + }; + const response = await request(app).post('/headquarters').send(newHeadquarters); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newHeadquarters); + expect(response.body.headquartersId).toBeDefined(); + }); + + it('should get all headquarters', async () => { + const response = await request(app).get('/headquarters'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get a headquarters by ID', async () => { + // First create a headquarters to test getting it + const newHeadquarters = { + name: 'Regional HQ', + description: 'Regional office', + address: '200 Regional Ave', + contactPerson: 'Charlie Brown', + email: 'charlie@regionalhq.com', + phone: '555-2000', + }; + const createResponse = await request(app).post('/headquarters').send(newHeadquarters); + const headquartersId = createResponse.body.headquartersId; + + const response = await request(app).get(`/headquarters/${headquartersId}`); + expect(response.status).toBe(200); + expect(response.body.headquartersId).toBe(headquartersId); + }); + + it('should update a headquarters by ID', async () => { + // First create a headquarters to test updating it + const newHeadquarters = { + name: 'Original HQ', + description: 'Original description', + address: '300 Original St', + contactPerson: 'Diana Ross', + email: 'diana@original.com', + phone: '555-3000', + }; + const createResponse = await request(app).post('/headquarters').send(newHeadquarters); + const headquartersId = createResponse.body.headquartersId; + + const updatedHeadquarters = { + ...newHeadquarters, + name: 'Updated Headquarters Name', + }; + const response = await request(app) + .put(`/headquarters/${headquartersId}`) + .send(updatedHeadquarters); + expect(response.status).toBe(200); + expect(response.body.name).toBe('Updated Headquarters Name'); + }); + + it('should delete a headquarters by ID', async () => { + // First create a headquarters to test deleting it + const newHeadquarters = { + name: 'HQ To Delete', + description: 'This headquarters will be deleted', + address: '400 Delete Ave', + contactPerson: 'Delete Person', + email: 'delete@hq.com', + phone: '555-4000', + }; + const createResponse = await request(app).post('/headquarters').send(newHeadquarters); + const headquartersId = createResponse.body.headquartersId; + + const response = await request(app).delete(`/headquarters/${headquartersId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing headquarters', async () => { + const response = await request(app).get('/headquarters/999'); + expect(response.status).toBe(404); + }); +}); diff --git a/api/src/routes/order.test.ts b/api/src/routes/order.test.ts new file mode 100644 index 0000000..4ccaf2d --- /dev/null +++ b/api/src/routes/order.test.ts @@ -0,0 +1,121 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import orderRouter from './order'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Order API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Seed required foreign keys: headquarters and branch + const db = await getDatabase(); + await db.run( + 'INSERT INTO headquarters (headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?)', + [1, 'Test HQ', 'Test HQ desc', '123 HQ St', 'HQ Contact', 'hq@test.com', '555-0000'], + ); + await db.run( + 'INSERT INTO branches (branch_id, headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [1, 1, 'Test Branch', 'Test branch desc', '456 Branch Ave', 'Branch Contact', 'branch@test.com', '555-0001'], + ); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/orders', orderRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new order', async () => { + const newOrder = { + branchId: 1, + orderDate: '2024-01-15T10:00:00Z', + name: 'Test Order', + description: 'Test order description', + status: 'pending', + }; + const response = await request(app).post('/orders').send(newOrder); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newOrder); + expect(response.body.orderId).toBeDefined(); + }); + + it('should get all orders', async () => { + const response = await request(app).get('/orders'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get an order by ID', async () => { + // First create an order to test getting it + const newOrder = { + branchId: 1, + orderDate: '2024-01-15T11:00:00Z', + name: 'Order To Get', + description: 'Test order', + status: 'processing', + }; + const createResponse = await request(app).post('/orders').send(newOrder); + const orderId = createResponse.body.orderId; + + const response = await request(app).get(`/orders/${orderId}`); + expect(response.status).toBe(200); + expect(response.body.orderId).toBe(orderId); + }); + + it('should update an order by ID', async () => { + // First create an order to test updating it + const newOrder = { + branchId: 1, + orderDate: '2024-01-15T12:00:00Z', + name: 'Original Order', + description: 'Original description', + status: 'pending', + }; + const createResponse = await request(app).post('/orders').send(newOrder); + const orderId = createResponse.body.orderId; + + const updatedOrder = { + ...newOrder, + status: 'shipped', + description: 'Updated description', + }; + const response = await request(app).put(`/orders/${orderId}`).send(updatedOrder); + expect(response.status).toBe(200); + expect(response.body.status).toBe('shipped'); + expect(response.body.description).toBe('Updated description'); + }); + + it('should delete an order by ID', async () => { + // First create an order to test deleting it + const newOrder = { + branchId: 1, + orderDate: '2024-01-15T13:00:00Z', + name: 'Order To Delete', + description: 'This order will be deleted', + status: 'cancelled', + }; + const createResponse = await request(app).post('/orders').send(newOrder); + const orderId = createResponse.body.orderId; + + const response = await request(app).delete(`/orders/${orderId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing order', async () => { + const response = await request(app).get('/orders/999'); + expect(response.status).toBe(404); + }); +}); diff --git a/api/src/routes/orderDetail.test.ts b/api/src/routes/orderDetail.test.ts new file mode 100644 index 0000000..4304c53 --- /dev/null +++ b/api/src/routes/orderDetail.test.ts @@ -0,0 +1,135 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import orderDetailRouter from './orderDetail'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Order Detail API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Seed required foreign keys: headquarters, branch, supplier, product, and order + const db = await getDatabase(); + await db.run( + 'INSERT INTO headquarters (headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?)', + [1, 'Test HQ', 'Test HQ desc', '123 HQ St', 'HQ Contact', 'hq@test.com', '555-0000'], + ); + await db.run( + 'INSERT INTO branches (branch_id, headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [1, 1, 'Test Branch', 'Test branch desc', '456 Branch Ave', 'Branch Contact', 'branch@test.com', '555-0001'], + ); + await db.run( + 'INSERT INTO suppliers (supplier_id, name, description, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?)', + [1, 'Test Supplier', 'Test supplier desc', 'Supplier Contact', 'supplier@test.com', '555-0002'], + ); + await db.run( + 'INSERT INTO products (product_id, supplier_id, name, description, price, sku, unit, img_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [1, 1, 'Test Product', 'Test product desc', 99.99, 'TEST-SKU', 'piece', 'test.jpg'], + ); + await db.run( + 'INSERT INTO orders (order_id, branch_id, order_date, name, description, status) VALUES (?, ?, ?, ?, ?, ?)', + [1, 1, '2024-01-15T10:00:00Z', 'Test Order', 'Test order desc', 'pending'], + ); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/order-details', orderDetailRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new order detail', async () => { + const newOrderDetail = { + orderId: 1, + productId: 1, + quantity: 5, + unitPrice: 99.99, + notes: 'Test order detail', + }; + const response = await request(app).post('/order-details').send(newOrderDetail); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newOrderDetail); + expect(response.body.orderDetailId).toBeDefined(); + }); + + it('should get all order details', async () => { + const response = await request(app).get('/order-details'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get an order detail by ID', async () => { + // First create an order detail to test getting it + const newOrderDetail = { + orderId: 1, + productId: 1, + quantity: 10, + unitPrice: 99.99, + notes: 'Order detail to get', + }; + const createResponse = await request(app).post('/order-details').send(newOrderDetail); + const orderDetailId = createResponse.body.orderDetailId; + + const response = await request(app).get(`/order-details/${orderDetailId}`); + expect(response.status).toBe(200); + expect(response.body.orderDetailId).toBe(orderDetailId); + }); + + it('should update an order detail by ID', async () => { + // First create an order detail to test updating it + const newOrderDetail = { + orderId: 1, + productId: 1, + quantity: 3, + unitPrice: 99.99, + notes: 'Original notes', + }; + const createResponse = await request(app).post('/order-details').send(newOrderDetail); + const orderDetailId = createResponse.body.orderDetailId; + + const updatedOrderDetail = { + ...newOrderDetail, + quantity: 7, + notes: 'Updated notes', + }; + const response = await request(app) + .put(`/order-details/${orderDetailId}`) + .send(updatedOrderDetail); + expect(response.status).toBe(200); + expect(response.body.quantity).toBe(7); + expect(response.body.notes).toBe('Updated notes'); + }); + + it('should delete an order detail by ID', async () => { + // First create an order detail to test deleting it + const newOrderDetail = { + orderId: 1, + productId: 1, + quantity: 2, + unitPrice: 99.99, + notes: 'Order detail to delete', + }; + const createResponse = await request(app).post('/order-details').send(newOrderDetail); + const orderDetailId = createResponse.body.orderDetailId; + + const response = await request(app).delete(`/order-details/${orderDetailId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing order detail', async () => { + const response = await request(app).get('/order-details/999'); + expect(response.status).toBe(404); + }); +}); diff --git a/api/src/routes/orderDetailDelivery.test.ts b/api/src/routes/orderDetailDelivery.test.ts new file mode 100644 index 0000000..83a5d75 --- /dev/null +++ b/api/src/routes/orderDetailDelivery.test.ts @@ -0,0 +1,151 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import orderDetailDeliveryRouter from './orderDetailDelivery'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Order Detail Delivery API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Seed required foreign keys: all entities needed for order detail delivery + const db = await getDatabase(); + await db.run( + 'INSERT INTO headquarters (headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?)', + [1, 'Test HQ', 'Test HQ desc', '123 HQ St', 'HQ Contact', 'hq@test.com', '555-0000'], + ); + await db.run( + 'INSERT INTO branches (branch_id, headquarters_id, name, description, address, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [1, 1, 'Test Branch', 'Test branch desc', '456 Branch Ave', 'Branch Contact', 'branch@test.com', '555-0001'], + ); + await db.run( + 'INSERT INTO suppliers (supplier_id, name, description, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?)', + [1, 'Test Supplier', 'Test supplier desc', 'Supplier Contact', 'supplier@test.com', '555-0002'], + ); + await db.run( + 'INSERT INTO products (product_id, supplier_id, name, description, price, sku, unit, img_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [1, 1, 'Test Product', 'Test product desc', 99.99, 'TEST-SKU', 'piece', 'test.jpg'], + ); + await db.run( + 'INSERT INTO orders (order_id, branch_id, order_date, name, description, status) VALUES (?, ?, ?, ?, ?, ?)', + [1, 1, '2024-01-15T10:00:00Z', 'Test Order', 'Test order desc', 'pending'], + ); + await db.run( + 'INSERT INTO order_details (order_detail_id, order_id, product_id, quantity, unit_price, notes) VALUES (?, ?, ?, ?, ?, ?)', + [1, 1, 1, 10, 99.99, 'Test order detail'], + ); + await db.run( + 'INSERT INTO deliveries (delivery_id, supplier_id, delivery_date, name, description, status) VALUES (?, ?, ?, ?, ?, ?)', + [1, 1, '2024-02-01T14:00:00Z', 'Test Delivery', 'Test delivery desc', 'pending'], + ); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/order-detail-deliveries', orderDetailDeliveryRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new order detail delivery', async () => { + const newOrderDetailDelivery = { + orderDetailId: 1, + deliveryId: 1, + quantity: 5, + notes: 'Test order detail delivery', + }; + const response = await request(app) + .post('/order-detail-deliveries') + .send(newOrderDetailDelivery); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newOrderDetailDelivery); + expect(response.body.orderDetailDeliveryId).toBeDefined(); + }); + + it('should get all order detail deliveries', async () => { + const response = await request(app).get('/order-detail-deliveries'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get an order detail delivery by ID', async () => { + // First create an order detail delivery to test getting it + const newOrderDetailDelivery = { + orderDetailId: 1, + deliveryId: 1, + quantity: 3, + notes: 'Order detail delivery to get', + }; + const createResponse = await request(app) + .post('/order-detail-deliveries') + .send(newOrderDetailDelivery); + const orderDetailDeliveryId = createResponse.body.orderDetailDeliveryId; + + const response = await request(app).get( + `/order-detail-deliveries/${orderDetailDeliveryId}`, + ); + expect(response.status).toBe(200); + expect(response.body.orderDetailDeliveryId).toBe(orderDetailDeliveryId); + }); + + it('should update an order detail delivery by ID', async () => { + // First create an order detail delivery to test updating it + const newOrderDetailDelivery = { + orderDetailId: 1, + deliveryId: 1, + quantity: 2, + notes: 'Original notes', + }; + const createResponse = await request(app) + .post('/order-detail-deliveries') + .send(newOrderDetailDelivery); + const orderDetailDeliveryId = createResponse.body.orderDetailDeliveryId; + + const updatedOrderDetailDelivery = { + ...newOrderDetailDelivery, + quantity: 8, + notes: 'Updated notes', + }; + const response = await request(app) + .put(`/order-detail-deliveries/${orderDetailDeliveryId}`) + .send(updatedOrderDetailDelivery); + expect(response.status).toBe(200); + expect(response.body.quantity).toBe(8); + expect(response.body.notes).toBe('Updated notes'); + }); + + it('should delete an order detail delivery by ID', async () => { + // First create an order detail delivery to test deleting it + const newOrderDetailDelivery = { + orderDetailId: 1, + deliveryId: 1, + quantity: 1, + notes: 'Order detail delivery to delete', + }; + const createResponse = await request(app) + .post('/order-detail-deliveries') + .send(newOrderDetailDelivery); + const orderDetailDeliveryId = createResponse.body.orderDetailDeliveryId; + + const response = await request(app).delete( + `/order-detail-deliveries/${orderDetailDeliveryId}`, + ); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing order detail delivery', async () => { + const response = await request(app).get('/order-detail-deliveries/999'); + expect(response.status).toBe(404); + }); +}); diff --git a/api/src/routes/product.test.ts b/api/src/routes/product.test.ts new file mode 100644 index 0000000..75342b6 --- /dev/null +++ b/api/src/routes/product.test.ts @@ -0,0 +1,152 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import productRouter from './product'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Product API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Seed required foreign key: supplier id 1 + const db = await getDatabase(); + await db.run( + 'INSERT INTO suppliers (supplier_id, name, description, contact_person, email, phone) VALUES (?, ?, ?, ?, ?, ?)', + [1, 'Test Supplier', 'Test description', 'Contact Person', 'contact@test.com', '555-0000'], + ); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/products', productRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new product', async () => { + const newProduct = { + supplierId: 1, + name: 'Test Product', + description: 'A test product', + price: 99.99, + sku: 'TEST-SKU-001', + unit: 'piece', + imgName: 'test.jpg', + }; + const response = await request(app).post('/products').send(newProduct); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newProduct); + expect(response.body.productId).toBeDefined(); + }); + + it('should get all products', async () => { + const response = await request(app).get('/products'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get a product by ID', async () => { + // First create a product to test getting it + const newProduct = { + supplierId: 1, + name: 'Product To Get', + description: 'Test product', + price: 49.99, + sku: 'TEST-SKU-002', + unit: 'box', + imgName: 'product.jpg', + }; + const createResponse = await request(app).post('/products').send(newProduct); + const productId = createResponse.body.productId; + + const response = await request(app).get(`/products/${productId}`); + expect(response.status).toBe(200); + expect(response.body.productId).toBe(productId); + }); + + it('should get a product by name', async () => { + // First create a product to test getting it by name + const newProduct = { + supplierId: 1, + name: 'Unique Product Name', + description: 'Test product', + price: 29.99, + sku: 'TEST-SKU-003', + unit: 'each', + imgName: 'unique.jpg', + }; + await request(app).post('/products').send(newProduct); + + const response = await request(app).get('/products/name/Unique Product Name'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBeGreaterThan(0); + expect(response.body[0].name).toBe('Unique Product Name'); + }); + + it('should update a product by ID', async () => { + // First create a product to test updating it + const newProduct = { + supplierId: 1, + name: 'Original Product', + description: 'Original description', + price: 19.99, + sku: 'TEST-SKU-004', + unit: 'unit', + imgName: 'original.jpg', + }; + const createResponse = await request(app).post('/products').send(newProduct); + const productId = createResponse.body.productId; + + const updatedProduct = { + ...newProduct, + name: 'Updated Product Name', + price: 24.99, + }; + const response = await request(app).put(`/products/${productId}`).send(updatedProduct); + expect(response.status).toBe(200); + expect(response.body.name).toBe('Updated Product Name'); + expect(response.body.price).toBe(24.99); + }); + + it('should delete a product by ID', async () => { + // First create a product to test deleting it + const newProduct = { + supplierId: 1, + name: 'Product To Delete', + description: 'This product will be deleted', + price: 9.99, + sku: 'TEST-SKU-005', + unit: 'item', + imgName: 'delete.jpg', + }; + const createResponse = await request(app).post('/products').send(newProduct); + const productId = createResponse.body.productId; + + const response = await request(app).delete(`/products/${productId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing product', async () => { + const response = await request(app).get('/products/999'); + expect(response.status).toBe(404); + }); + + it('should return empty array for non-existing product by name', async () => { + const response = await request(app).get('/products/name/NonExistentProduct'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(0); + }); +}); diff --git a/api/src/routes/supplier.test.ts b/api/src/routes/supplier.test.ts new file mode 100644 index 0000000..1c049ad --- /dev/null +++ b/api/src/routes/supplier.test.ts @@ -0,0 +1,108 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import request from 'supertest'; +import express from 'express'; +import supplierRouter from './supplier'; +import { runMigrations } from '../db/migrate'; +import { closeDatabase, getDatabase } from '../db/sqlite'; +import { errorHandler } from '../utils/errors'; + +let app: express.Express; + +describe('Supplier API', () => { + beforeEach(async () => { + // Ensure a fresh in-memory database for each test + await closeDatabase(); + await getDatabase(true); + await runMigrations(true); + + // Set up express app + app = express(); + app.use(express.json()); + app.use('/suppliers', supplierRouter); + // Attach error handler to translate repo errors + app.use(errorHandler); + }); + + afterEach(async () => { + await closeDatabase(); + }); + + it('should create a new supplier', async () => { + const newSupplier = { + name: 'Test Supplier Co', + description: 'A test supplier', + contactPerson: 'John Doe', + email: 'john@testsupplier.com', + phone: '555-0100', + }; + const response = await request(app).post('/suppliers').send(newSupplier); + expect(response.status).toBe(201); + expect(response.body).toMatchObject(newSupplier); + expect(response.body.supplierId).toBeDefined(); + }); + + it('should get all suppliers', async () => { + const response = await request(app).get('/suppliers'); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + }); + + it('should get a supplier by ID', async () => { + // First create a supplier to test getting it + const newSupplier = { + name: 'Supplier To Get', + description: 'Test supplier', + contactPerson: 'Jane Smith', + email: 'jane@test.com', + phone: '555-0200', + }; + const createResponse = await request(app).post('/suppliers').send(newSupplier); + const supplierId = createResponse.body.supplierId; + + const response = await request(app).get(`/suppliers/${supplierId}`); + expect(response.status).toBe(200); + expect(response.body.supplierId).toBe(supplierId); + }); + + it('should update a supplier by ID', async () => { + // First create a supplier to test updating it + const newSupplier = { + name: 'Original Supplier', + description: 'Original description', + contactPerson: 'Bob Jones', + email: 'bob@original.com', + phone: '555-0300', + }; + const createResponse = await request(app).post('/suppliers').send(newSupplier); + const supplierId = createResponse.body.supplierId; + + const updatedSupplier = { + ...newSupplier, + name: 'Updated Supplier Name', + }; + const response = await request(app).put(`/suppliers/${supplierId}`).send(updatedSupplier); + expect(response.status).toBe(200); + expect(response.body.name).toBe('Updated Supplier Name'); + }); + + it('should delete a supplier by ID', async () => { + // First create a supplier to test deleting it + const newSupplier = { + name: 'Supplier To Delete', + description: 'This supplier will be deleted', + contactPerson: 'Delete Person', + email: 'delete@test.com', + phone: '555-0400', + }; + const createResponse = await request(app).post('/suppliers').send(newSupplier); + const supplierId = createResponse.body.supplierId; + + const response = await request(app).delete(`/suppliers/${supplierId}`); + expect(response.status).toBe(204); + }); + + it('should return 404 for non-existing supplier', async () => { + const response = await request(app).get('/suppliers/999'); + expect(response.status).toBe(404); + }); +});