Added unit tests.
Some checks failed
farmcontrol/farmcontrol-api/pipeline/head There was a failure building this commit

This commit is contained in:
Tom Butcher 2025-12-29 00:39:13 +00:00
parent 2d156aa98f
commit 28c94159b4
43 changed files with 5346 additions and 36 deletions

16
Jenkinsfile vendored
View File

@ -31,6 +31,22 @@ pipeline {
} }
} }
stage('Run Tests') {
steps {
nodejs(nodeJSInstallationName: 'Node23') {
sh '''
export NODE_ENV=test
yarn test
'''
}
}
post {
always {
junit 'test-results.xml'
}
}
}
stage('Deploy via SSH') { stage('Deploy via SSH') {
steps { steps {
sshPublisher(publishers: [ sshPublisher(publishers: [

4
babel.config.cjs Normal file
View File

@ -0,0 +1,4 @@
module.exports = {
presets: [['@babel/preset-env', { targets: { node: 'current' } }]],
plugins: ['transform-import-meta'],
};

View File

@ -48,6 +48,54 @@
}, },
"otpExpiryMins": 0.5 "otpExpiryMins": 0.5
}, },
"test": {
"server": {
"port": 8788,
"logLevel": "error"
},
"auth": {
"enabled": false,
"keycloak": {
"url": "http://localhost:8080",
"realm": "test",
"clientId": "test-client"
},
"requiredRoles": []
},
"app": {
"urlClient": "http://localhost:3000",
"urlElectronClient": "http://localhost:5780",
"urlApi": "http://localhost:8788/api",
"devAuthClient": "http://localhost:3500"
},
"database": {
"mongo": {
"url": "mongodb://127.0.0.1:27017/farmcontrol-test",
"link": "127.0.0.1:27017"
},
"redis": {
"host": "localhost",
"port": 6379,
"password": "",
"cacheTtl": 30
},
"nats": {
"host": "localhost",
"port": 4222
}
},
"storage": {
"fileStorage": "./test-uploads",
"ceph": {
"accessKeyId": "minioadmin",
"secretAccessKey": "minioadmin123",
"endpoint": "http://127.0.0.1:9000",
"region": "us-east-1",
"filesBucket": "farmcontrol-test"
}
},
"otpExpiryMins": 0.5
},
"production": { "production": {
"server": { "server": {
"port": 8080, "port": 8080,

22
jest.config.cjs Normal file
View File

@ -0,0 +1,22 @@
module.exports = {
testEnvironment: 'node',
transform: {},
moduleFileExtensions: ['js', 'json', 'jsx', 'node'],
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
verbose: true,
reporters: [
'default',
[
'jest-junit',
{
outputDirectory: '.',
outputName: 'test-results.xml',
suiteName: 'farmcontrol-api-tests',
classNameTemplate: '{classname}',
titleTemplate: '{title}',
ancestorSeparator: ' ',
usePathForSuiteName: 'true',
},
],
],
};

View File

@ -41,20 +41,26 @@
"@babel/plugin-proposal-object-rest-spread": "^7.20.7", "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
"@babel/preset-env": "^7.28.5", "@babel/preset-env": "^7.28.5",
"@babel/register": "^7.28.3", "@babel/register": "^7.28.3",
"@jest/globals": "^30.2.0",
"babel-jest": "^30.2.0",
"babel-plugin-transform-import-meta": "^2.3.3",
"concurrently": "^9.2.1", "concurrently": "^9.2.1",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-prettier": "^5.5.4", "eslint-plugin-prettier": "^5.5.4",
"jest": "^30.2.0",
"jest-junit": "^16.0.0",
"prettier": "^3.6.2", "prettier": "^3.6.2",
"sequelize-cli": "^6.6.3", "sequelize-cli": "^6.6.3",
"standard": "^17.1.2" "standard": "^17.1.2",
"supertest": "^7.1.4"
}, },
"scripts": { "scripts": {
"syncModelsWithWS": "node fcdev.js", "syncModelsWithWS": "node fcdev.js",
"watch:schemas": "nodemon --config nodemon.schemas.json", "watch:schemas": "nodemon --config nodemon.schemas.json",
"dev": "concurrently --names \"API,SCHEMAS\" --prefix-colors \"cyan,yellow\" \"nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js\" \"nodemon --config nodemon.schemas.json\"", "dev": "concurrently --names \"API,SCHEMAS\" --prefix-colors \"cyan,yellow\" \"nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js\" \"nodemon --config nodemon.schemas.json\"",
"dev:api": "nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js", "dev:api": "nodemon --exec babel-node --experimental-specifier-resolution=node src/index.js",
"test": "echo \"Error: no test specified\" && exit 1", "test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
"seed": "node src/mongo/seedData.js", "seed": "node src/mongo/seedData.js",
"clear": "node src/mongo/clearDbs.js" "clear": "node src/mongo/clearDbs.js"
}, },

View File

@ -0,0 +1,68 @@
import { jest } from '@jest/globals';
import request from 'supertest';
// Mock Keycloak and Auth
jest.unstable_mockModule('../keycloak.js', () => ({
keycloak: {
middleware: () => (req, res, next) => next(),
protect: () => (req, res, next) => next(),
},
isAuthenticated: (req, res, next) => next(),
expressSession: (req, res, next) => next(),
}));
// Mock database connections and initializations in index.js
jest.unstable_mockModule('../database/mongo.js', () => ({
dbConnect: jest.fn(),
}));
jest.unstable_mockModule('../database/nats.js', () => ({
natsServer: { connect: jest.fn() },
}));
jest.unstable_mockModule('../database/ceph.js', () => ({
initializeBuckets: jest.fn(),
uploadFile: jest.fn(),
downloadFile: jest.fn(),
deleteFile: jest.fn(),
fileExists: jest.fn(),
listFiles: jest.fn(),
getFileMetadata: jest.fn(),
getPresignedUrl: jest.fn(),
BUCKETS: { FILES: 'test-bucket' },
}));
// Mock the service handlers to avoid database calls
jest.unstable_mockModule('../services/management/users.js', () => ({
listUsersRouteHandler: jest.fn((req, res) => res.send([{ id: '1', name: 'Mock User' }])),
listUsersByPropertiesRouteHandler: jest.fn(),
getUserRouteHandler: jest.fn((req, res) => res.send({ id: req.params.id, name: 'Mock User' })),
editUserRouteHandler: jest.fn(),
getUserStatsRouteHandler: jest.fn(),
getUserHistoryRouteHandler: jest.fn(),
}));
const { default: app } = await import('../index.js');
const { listUsersRouteHandler, getUserRouteHandler } = await import(
'../services/management/users.js'
);
describe('Users API Endpoints', () => {
describe('GET /users', () => {
it('should return a list of users', async () => {
const response = await request(app).get('/users');
expect(response.status).toBe(200);
expect(response.body).toEqual([{ id: '1', name: 'Mock User' }]);
expect(listUsersRouteHandler).toHaveBeenCalled();
});
});
describe('GET /users/:id', () => {
it('should return a single user', async () => {
const response = await request(app).get('/users/123');
expect(response.status).toBe(200);
expect(response.body).toEqual({ id: '123', name: 'Mock User' });
expect(getUserRouteHandler).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,163 @@
import { jest } from '@jest/globals';
// Mock src/database/utils.js (where generateId lives)
jest.unstable_mockModule('../utils.js', () => ({
generateId: jest.fn(() => () => 'test-id'),
}));
// Mock src/utils.js (where most database.js helpers live)
jest.unstable_mockModule('../../utils.js', () => ({
deleteAuditLog: jest.fn(),
distributeDelete: jest.fn(),
expandObjectIds: jest.fn((obj) => obj),
modelHasRef: jest.fn(() => false),
getFieldsByRef: jest.fn(() => []),
getQueryToCacheKey: jest.fn(({ model, id }) => `${model}:${id}`),
editAuditLog: jest.fn(),
distributeUpdate: jest.fn(),
newAuditLog: jest.fn(),
distributeNew: jest.fn(),
distributeChildUpdate: jest.fn(),
distributeChildDelete: jest.fn(),
distributeChildNew: jest.fn(),
distributeStats: jest.fn(),
}));
jest.unstable_mockModule('../redis.js', () => ({
redisServer: {
getKey: jest.fn(),
setKey: jest.fn(),
deleteKey: jest.fn(),
getKeysByPattern: jest.fn(() => []), // Return empty array to avoid iterable error
},
}));
jest.unstable_mockModule('../../services/misc/model.js', () => ({
getAllModels: jest.fn(() => []),
}));
jest.unstable_mockModule('../schemas/management/auditlog.schema.js', () => ({
auditLogModel: {
find: jest.fn(),
create: jest.fn(),
},
}));
// Mock fileModel specifically as it's imported by database.js
jest.unstable_mockModule('../schemas/management/file.schema.js', () => ({
fileModel: {
findById: jest.fn(),
},
}));
// Now import the database utilities
const { listObjects, getObject, newObject, editObject, deleteObject } = await import(
'../database.js'
);
describe('Database Utilities (CRUD)', () => {
let mockModel;
beforeEach(() => {
mockModel = {
modelName: 'TestModel',
find: jest.fn().mockReturnThis(),
findById: jest.fn().mockReturnThis(),
findByIdAndUpdate: jest.fn().mockReturnThis(),
findByIdAndDelete: jest.fn().mockReturnThis(),
create: jest.fn(),
sort: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
populate: jest.fn().mockReturnThis(),
select: jest.fn().mockReturnThis(),
lean: jest.fn().mockReturnThis(),
exec: jest.fn(),
};
jest.clearAllMocks();
});
describe('listObjects', () => {
it('should return a list of objects', async () => {
const mockData = [{ _id: '1', name: 'Test' }];
mockModel.lean.mockResolvedValue(mockData);
const result = await listObjects({ model: mockModel });
expect(mockModel.find).toHaveBeenCalled();
expect(result).toEqual(mockData);
});
it('should handle pagination', async () => {
await listObjects({ model: mockModel, page: 2, limit: 10 });
expect(mockModel.skip).toHaveBeenCalledWith(10);
expect(mockModel.limit).toHaveBeenCalledWith(10);
});
});
describe('getObject', () => {
it('should return a single object by ID', async () => {
const mockData = { _id: '123', name: 'Test' };
mockModel.lean.mockResolvedValue(mockData);
const result = await getObject({ model: mockModel, id: '123' });
expect(mockModel.findById).toHaveBeenCalledWith('123');
expect(result).toEqual(mockData);
});
it('should return 404 if object not found', async () => {
mockModel.lean.mockResolvedValue(null);
const result = await getObject({ model: mockModel, id: '123' });
expect(result).toEqual({ error: 'Object not found.', code: 404 });
});
});
describe('newObject', () => {
it('should create a new object', async () => {
const newData = { name: 'New' };
const createdData = { _id: '456', ...newData };
mockModel.create.mockResolvedValue({
toObject: () => createdData,
_id: '456',
});
const result = await newObject({ model: mockModel, newData });
expect(mockModel.create).toHaveBeenCalledWith(newData);
expect(result).toEqual(createdData);
});
});
describe('editObject', () => {
it('should update an existing object', async () => {
const id = '123';
const updateData = { name: 'Updated' };
const previousData = { _id: id, name: 'Old' };
mockModel.lean.mockResolvedValue(previousData);
const result = await editObject({ model: mockModel, id, updateData });
expect(mockModel.findByIdAndUpdate).toHaveBeenCalledWith(id, updateData);
expect(result).toEqual({ ...previousData, ...updateData });
});
});
describe('deleteObject', () => {
it('should delete an object', async () => {
const id = '123';
const mockData = { _id: id, name: 'To be deleted' };
mockModel.findByIdAndDelete.mockResolvedValue({
toObject: () => mockData,
});
const result = await deleteObject({ model: mockModel, id });
expect(mockModel.findByIdAndDelete).toHaveBeenCalledWith(id);
expect(result).toEqual({ deleted: true, object: mockData });
});
});
});

View File

@ -150,4 +150,8 @@ app.use('/salesorders', salesOrderRoutes);
app.use('/notes', noteRoutes); app.use('/notes', noteRoutes);
// Start the application // Start the application
if (process.env.NODE_ENV !== 'test') {
initializeApp(); initializeApp();
}
export default app;

View File

@ -0,0 +1,154 @@
import { jest } from '@jest/globals';
// Mock dependencies
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
checkStates: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/finance/invoice.schema.js', () => ({
invoiceModel: { modelName: 'Invoice' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({
orderItemModel: { modelName: 'OrderItem' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/shipment.schema.js', () => ({
shipmentModel: { modelName: 'Shipment' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
// Import handlers after mocking
const {
listInvoicesRouteHandler,
getInvoiceRouteHandler,
newInvoiceRouteHandler,
editInvoiceRouteHandler,
deleteInvoiceRouteHandler,
postInvoiceRouteHandler,
} = await import('../invoices.js');
const { listObjects, getObject, editObject, newObject, deleteObject, checkStates } = await import(
'../../../database/database.js'
);
const { invoiceModel } = await import('../../../database/schemas/finance/invoice.schema.js');
describe('Invoice Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listInvoicesRouteHandler', () => {
it('should list invoices', async () => {
const mockResult = [{ _id: '1', invoiceNumber: 'INV-001' }];
listObjects.mockResolvedValue(mockResult);
await listInvoicesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getInvoiceRouteHandler', () => {
it('should get an invoice by ID', async () => {
req.params.id = '123';
const mockResult = { _id: '123', invoiceNumber: 'INV-001' };
getObject.mockResolvedValue(mockResult);
await getInvoiceRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newInvoiceRouteHandler', () => {
it('should create a new invoice with order items and shipments', async () => {
req.body = { order: 'order123', orderType: 'sales' };
// Mock listObjects for orderItems and shipments
listObjects.mockResolvedValueOnce([{ _id: 'oi1', totalAmount: 100, invoicedAmount: 0 }]); // orderItems
listObjects.mockResolvedValueOnce([{ _id: 's1', amount: 20, invoicedAmount: 0 }]); // shipments
const mockInvoice = { _id: 'inv456' };
newObject.mockResolvedValue(mockInvoice);
await newInvoiceRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockInvoice);
});
});
describe('postInvoiceRouteHandler', () => {
it('should post a draft invoice and update order items/shipments', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
const mockInvoice = {
_id: '507f1f77bcf86cd799439011',
invoiceOrderItems: [{ orderItem: { _id: 'oi1' }, invoiceAmount: 50, invoiceQuantity: 1 }],
invoiceShipments: [{ shipment: { _id: 's1' }, invoiceAmount: 10 }],
};
getObject.mockResolvedValueOnce(mockInvoice);
// Mock getObject for individual orderItems and shipments
getObject.mockResolvedValueOnce({ _id: 'oi1', invoicedAmount: 0, invoicedQuantity: 0 });
getObject.mockResolvedValueOnce({ _id: 's1', invoicedAmount: 0 });
editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'sent' } });
await postInvoiceRouteHandler(req, res);
expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['draft'] }));
expect(editObject).toHaveBeenCalled(); // Should be called for items, shipments, and the invoice itself
expect(res.send).toHaveBeenCalled();
});
it('should fail if invoice is not in draft state', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(false);
await postInvoiceRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({ error: 'Invoice is not in draft state.' })
);
});
});
});

View File

@ -0,0 +1,124 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
checkStates: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/finance/payment.schema.js', () => ({
paymentModel: { modelName: 'Payment' },
}));
jest.unstable_mockModule('../../../database/schemas/finance/invoice.schema.js', () => ({
invoiceModel: { modelName: 'Invoice' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listPaymentsRouteHandler,
getPaymentRouteHandler,
newPaymentRouteHandler,
postPaymentRouteHandler,
} = await import('../payments.js');
const { listObjects, getObject, editObject, newObject, checkStates } = await import(
'../../../database/database.js'
);
const { paymentModel } = await import('../../../database/schemas/finance/payment.schema.js');
const { invoiceModel } = await import('../../../database/schemas/finance/invoice.schema.js');
describe('Payment Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listPaymentsRouteHandler', () => {
it('should list payments', async () => {
const mockResult = [{ _id: '1', amount: 100 }];
listObjects.mockResolvedValue(mockResult);
await listPaymentsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newPaymentRouteHandler', () => {
it('should create a new payment with invoice data', async () => {
req.body = { invoice: 'inv123', amount: 100 };
const mockInvoice = { _id: 'inv123', vendor: { _id: 'v1' }, client: { _id: 'c1' } };
getObject.mockResolvedValueOnce(mockInvoice);
const mockPayment = { _id: 'pay456', ...req.body };
newObject.mockResolvedValue(mockPayment);
await newPaymentRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: 'inv123' }));
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockPayment);
});
});
describe('postPaymentRouteHandler', () => {
it('should post a draft payment', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
const mockPayment = { _id: '507f1f77bcf86cd799439011', invoice: 'inv123' };
getObject.mockResolvedValueOnce(mockPayment);
getObject.mockResolvedValueOnce({ _id: 'inv123' });
editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'posted' } });
await postPaymentRouteHandler(req, res);
expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['draft'] }));
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalled();
});
it('should fail if payment is not in draft state', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(false);
await postPaymentRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({ error: 'Payment is not in draft state.' })
);
});
});
});

View File

@ -0,0 +1,86 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/filamentstock.schema.js', () => ({
filamentStockModel: { modelName: 'FilamentStock' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listFilamentStocksRouteHandler,
getFilamentStockRouteHandler,
newFilamentStockRouteHandler,
} = await import('../filamentstocks.js');
const { listObjects, getObject, newObject } = await import('../../../database/database.js');
const { filamentStockModel } = await import(
'../../../database/schemas/inventory/filamentstock.schema.js'
);
describe('Filament Stock Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listFilamentStocksRouteHandler', () => {
it('should list filament stocks', async () => {
const mockResult = [{ _id: '1', currentWeight: 500 }];
listObjects.mockResolvedValue(mockResult);
await listFilamentStocksRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: filamentStockModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newFilamentStockRouteHandler', () => {
it('should create a new filament stock', async () => {
req.body = { filament: 'filament123', currentWeight: 1000 };
const mockStock = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockStock);
await newFilamentStockRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockStock);
});
});
});

View File

@ -0,0 +1,100 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({
orderItemModel: { modelName: 'OrderItem' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listOrderItemsRouteHandler,
getOrderItemRouteHandler,
newOrderItemRouteHandler,
editOrderItemRouteHandler,
deleteOrderItemRouteHandler,
} = await import('../orderitems.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { orderItemModel } = await import('../../../database/schemas/inventory/orderitem.schema.js');
describe('Order Item Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listOrderItemsRouteHandler', () => {
it('should list order items', async () => {
const mockResult = [{ _id: '1', name: 'Order Item 1' }];
listObjects.mockResolvedValue(mockResult);
await listOrderItemsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: orderItemModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newOrderItemRouteHandler', () => {
it('should create a new order item', async () => {
req.body = { name: 'New Item', quantity: 10, order: 'order123' };
const mockItem = { _id: '456', ...req.body, state: { type: 'draft' } };
newObject.mockResolvedValue(mockItem);
await newOrderItemRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockItem);
});
});
describe('editOrderItemRouteHandler', () => {
it('should update an order item', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { quantity: 20 };
const mockResult = { _id: '507f1f77bcf86cd799439011', quantity: 20 };
editObject.mockResolvedValue(mockResult);
await editOrderItemRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,88 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/partstock.schema.js', () => ({
partStockModel: { modelName: 'PartStock' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listPartStocksRouteHandler,
getPartStockRouteHandler,
newPartStockRouteHandler,
editPartStockRouteHandler,
deletePartStockRouteHandler,
} = await import('../partstocks.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { partStockModel } = await import('../../../database/schemas/inventory/partstock.schema.js');
describe('Part Stock Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listPartStocksRouteHandler', () => {
it('should list part stocks', async () => {
const mockResult = [{ _id: '1', currentQuantity: 100 }];
listObjects.mockResolvedValue(mockResult);
await listPartStocksRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: partStockModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newPartStockRouteHandler', () => {
it('should create a new part stock', async () => {
req.body = { part: 'part123', currentQuantity: 50 };
const mockStock = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockStock);
await newPartStockRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockStock);
});
});
});

View File

@ -0,0 +1,122 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
checkStates: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/purchaseorder.schema.js', () => ({
purchaseOrderModel: { modelName: 'PurchaseOrder' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({
orderItemModel: { modelName: 'OrderItem' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/shipment.schema.js', () => ({
shipmentModel: { modelName: 'Shipment' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listPurchaseOrdersRouteHandler,
getPurchaseOrderRouteHandler,
newPurchaseOrderRouteHandler,
postPurchaseOrderRouteHandler,
} = await import('../purchaseorders.js');
const { listObjects, getObject, editObject, newObject, checkStates } = await import(
'../../../database/database.js'
);
const { purchaseOrderModel } = await import(
'../../../database/schemas/inventory/purchaseorder.schema.js'
);
describe('Purchase Order Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listPurchaseOrdersRouteHandler', () => {
it('should list purchase orders', async () => {
const mockResult = [{ _id: '1', reference: 'PO-001' }];
listObjects.mockResolvedValue(mockResult);
await listPurchaseOrdersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('postPurchaseOrderRouteHandler', () => {
it('should post a draft purchase order and update items/shipments', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
listObjects.mockResolvedValueOnce([
{ _id: 'oi1', state: { type: 'draft' }, shipment: 's1', _reference: 'ITEM1' },
]);
listObjects.mockResolvedValueOnce([
{ _id: 's1', state: { type: 'draft' }, _reference: 'SHIP1' },
]);
editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'sent' } });
await postPurchaseOrderRouteHandler(req, res);
expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['draft'] }));
expect(editObject).toHaveBeenCalledTimes(3);
expect(res.send).toHaveBeenCalled();
});
it('should fail if an order item is not in draft state', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
listObjects.mockResolvedValueOnce([
{ _id: 'oi1', state: { type: 'ordered' }, _reference: 'ITEM1' },
]);
listObjects.mockResolvedValueOnce([]);
await postPurchaseOrderRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({ error: 'Order item ITEM1 not in draft state.' })
);
});
});
});

View File

@ -0,0 +1,111 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
checkStates: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/shipment.schema.js', () => ({
shipmentModel: { modelName: 'Shipment' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({
orderItemModel: { modelName: 'OrderItem' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listShipmentsRouteHandler,
getShipmentRouteHandler,
newShipmentRouteHandler,
shipShipmentRouteHandler,
} = await import('../shipments.js');
const { listObjects, getObject, editObject, newObject, checkStates } = await import(
'../../../database/database.js'
);
const { shipmentModel } = await import('../../../database/schemas/inventory/shipment.schema.js');
const { orderItemModel } = await import('../../../database/schemas/inventory/orderitem.schema.js');
describe('Shipment Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listShipmentsRouteHandler', () => {
it('should list shipments', async () => {
const mockResult = [{ _id: '1', trackingNumber: 'TRACK123' }];
listObjects.mockResolvedValue(mockResult);
await listShipmentsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: shipmentModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newShipmentRouteHandler', () => {
it('should create a new shipment', async () => {
req.body = { order: 'order123', trackingNumber: 'TRACK456' };
const mockShipment = { _id: '456', ...req.body, state: { type: 'draft' } };
newObject.mockResolvedValue(mockShipment);
await newShipmentRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockShipment);
});
});
describe('shipShipmentRouteHandler', () => {
it('should ship a planned shipment and update order items', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
listObjects.mockResolvedValue([
{ _id: 'oi1', state: { type: 'ordered' } },
{ _id: 'oi2', state: { type: 'ordered' } },
]);
editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'shipped' } });
await shipShipmentRouteHandler(req, res);
expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['planned'] }));
expect(editObject).toHaveBeenCalledTimes(3); // 2 order items + 1 shipment
expect(res.send).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,91 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../utils.js', () => ({
getAuditLogs: jest.fn(),
}));
jest.unstable_mockModule('../../../database/database.js', () => ({
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/stockaudit.schema.js', () => ({
stockAuditModel: {
modelName: 'StockAudit',
aggregate: jest.fn(),
findOne: jest.fn(),
create: jest.fn(),
},
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listStockAuditsRouteHandler,
getStockAuditRouteHandler,
newStockAuditRouteHandler,
} = await import('../stockaudits.js');
const { getAuditLogs } = await import('../../../utils.js');
const { stockAuditModel } = await import('../../../database/schemas/inventory/stockaudit.schema.js');
describe('Stock Audit Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listStockAuditsRouteHandler', () => {
it('should list stock audits', async () => {
const mockResult = [{ _id: '1', type: 'full' }];
stockAuditModel.aggregate.mockResolvedValue(mockResult);
await listStockAuditsRouteHandler(req, res);
expect(stockAuditModel.aggregate).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getStockAuditRouteHandler', () => {
it('should get a stock audit by ID with audit logs', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockAudit = { _id: '507f1f77bcf86cd799439011', type: 'full', _doc: {} };
stockAuditModel.findOne.mockReturnValue({
populate: jest.fn().mockReturnValue({
populate: jest.fn().mockReturnValue({
populate: jest.fn().mockResolvedValue(mockAudit),
}),
}),
});
getAuditLogs.mockResolvedValue([]);
await getStockAuditRouteHandler(req, res);
expect(getAuditLogs).toHaveBeenCalled();
expect(res.send).toHaveBeenCalled();
});
});
});

View File

@ -0,0 +1,84 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/inventory/stockevent.schema.js', () => ({
stockEventModel: { modelName: 'StockEvent' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listStockEventsRouteHandler,
getStockEventRouteHandler,
newStockEventRouteHandler,
} = await import('../stockevents.js');
const { listObjects, getObject, newObject } = await import('../../../database/database.js');
const { stockEventModel } = await import('../../../database/schemas/inventory/stockevent.schema.js');
describe('Stock Event Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listStockEventsRouteHandler', () => {
it('should list stock events', async () => {
const mockResult = [{ _id: '1', type: 'adjustment' }];
listObjects.mockResolvedValue(mockResult);
await listStockEventsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: stockEventModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newStockEventRouteHandler', () => {
it('should create a new stock event', async () => {
req.body = { type: 'adjustment', value: 10 };
const mockEvent = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockEvent);
await newStockEventRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockEvent);
});
});
});

View File

@ -0,0 +1,87 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/auditlog.schema.js', () => ({
auditLogModel: {
modelName: 'AuditLog',
find: jest.fn(),
findOne: jest.fn(),
},
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const { listAuditLogsRouteHandler, getAuditLogRouteHandler } = await import('../auditlogs.js');
const { auditLogModel } = await import('../../../database/schemas/management/auditlog.schema.js');
describe('Audit Log Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listAuditLogsRouteHandler', () => {
it('should list audit logs', async () => {
const mockResult = [
{ _id: '1', operation: 'edit', parent: 'parent123', _doc: { parent: 'parent123' } },
];
auditLogModel.find.mockReturnValue({
sort: jest.fn().mockReturnThis(),
skip: jest.fn().mockReturnThis(),
limit: jest.fn().mockReturnThis(),
populate: jest.fn().mockResolvedValue(mockResult),
});
await listAuditLogsRouteHandler(req, res);
expect(auditLogModel.find).toHaveBeenCalled();
expect(res.send).toHaveBeenCalled();
});
});
describe('getAuditLogRouteHandler', () => {
it('should get an audit log by ID', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockLog = { _id: '507f1f77bcf86cd799439011', operation: 'edit' };
auditLogModel.findOne.mockReturnValue({
populate: jest.fn().mockReturnValue({
populate: jest.fn().mockReturnValue({
populate: jest.fn().mockResolvedValue(mockLog),
}),
}),
});
await getAuditLogRouteHandler(req, res);
expect(auditLogModel.findOne).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockLog);
});
});
});

View File

@ -0,0 +1,84 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/courier.schema.js', () => ({
courierModel: { modelName: 'Courier' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listCouriersRouteHandler,
getCourierRouteHandler,
newCourierRouteHandler,
editCourierRouteHandler,
} = await import('../courier.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { courierModel } = await import('../../../database/schemas/management/courier.schema.js');
describe('Courier Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listCouriersRouteHandler', () => {
it('should list couriers', async () => {
const mockResult = [{ _id: '1', name: 'FedEx' }];
listObjects.mockResolvedValue(mockResult);
await listCouriersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: courierModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newCourierRouteHandler', () => {
it('should create a new courier', async () => {
req.body = { name: 'DHL', email: 'contact@dhl.com' };
const mockCourier = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockCourier);
await newCourierRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockCourier);
});
});
});

View File

@ -0,0 +1,88 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/courierservice.schema.js', () => ({
courierServiceModel: { modelName: 'CourierService' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listCourierServicesRouteHandler,
getCourierServiceRouteHandler,
newCourierServiceRouteHandler,
editCourierServiceRouteHandler,
} = await import('../courierservice.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { courierServiceModel } = await import(
'../../../database/schemas/management/courierservice.schema.js'
);
describe('Courier Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listCourierServicesRouteHandler', () => {
it('should list courier services', async () => {
const mockResult = [{ _id: '1', name: 'Express' }];
listObjects.mockResolvedValue(mockResult);
await listCourierServicesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: courierServiceModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newCourierServiceRouteHandler', () => {
it('should create a new courier service', async () => {
req.body = { courier: 'courier123', name: 'Express Delivery' };
const mockService = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockService);
await newCourierServiceRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockService);
});
});
});

View File

@ -0,0 +1,85 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/documentjob.schema.js', () => ({
documentJobModel: { modelName: 'DocumentJob' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listDocumentJobsRouteHandler,
getDocumentJobRouteHandler,
newDocumentJobRouteHandler,
} = await import('../documentjobs.js');
const { listObjects, getObject, newObject } = await import('../../../database/database.js');
const { documentJobModel } = await import(
'../../../database/schemas/management/documentjob.schema.js'
);
describe('Document Job Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listDocumentJobsRouteHandler', () => {
it('should list document jobs', async () => {
const mockResult = [{ _id: '1', state: { type: 'pending' } }];
listObjects.mockResolvedValue(mockResult);
await listDocumentJobsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: documentJobModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newDocumentJobRouteHandler', () => {
it('should create a new document job', async () => {
req.body = { documentTemplate: 'template123', documentPrinter: 'printer123' };
const mockJob = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockJob);
await newDocumentJobRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockJob);
});
});
});

View File

@ -0,0 +1,85 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/documentprinter.schema.js', () => ({
documentPrinterModel: { modelName: 'DocumentPrinter' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listDocumentPrintersRouteHandler,
getDocumentPrinterRouteHandler,
newDocumentPrinterRouteHandler,
} = await import('../documentprinters.js');
const { listObjects, getObject, newObject } = await import('../../../database/database.js');
const { documentPrinterModel } = await import(
'../../../database/schemas/management/documentprinter.schema.js'
);
describe('Document Printer Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listDocumentPrintersRouteHandler', () => {
it('should list document printers', async () => {
const mockResult = [{ _id: '1', name: 'HP LaserJet' }];
listObjects.mockResolvedValue(mockResult);
await listDocumentPrintersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: documentPrinterModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newDocumentPrinterRouteHandler', () => {
it('should create a new document printer', async () => {
req.body = { name: 'Canon Printer', host: 'host123' };
const mockPrinter = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockPrinter);
await newDocumentPrinterRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockPrinter);
});
});
});

View File

@ -0,0 +1,88 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/documentsize.schema.js', () => ({
documentSizeModel: { modelName: 'DocumentSize' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listDocumentSizesRouteHandler,
getDocumentSizeRouteHandler,
newDocumentSizeRouteHandler,
editDocumentSizeRouteHandler,
} = await import('../documentsizes.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { documentSizeModel } = await import(
'../../../database/schemas/management/documentsize.schema.js'
);
describe('Document Size Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listDocumentSizesRouteHandler', () => {
it('should list document sizes', async () => {
const mockResult = [{ _id: '1', name: 'A4', width: 210, height: 297 }];
listObjects.mockResolvedValue(mockResult);
await listDocumentSizesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: documentSizeModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newDocumentSizeRouteHandler', () => {
it('should create a new document size', async () => {
req.body = { name: 'Letter', width: 216, height: 279 };
const mockSize = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockSize);
await newDocumentSizeRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockSize);
});
});
});

View File

@ -0,0 +1,88 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/documenttemplate.schema.js', () => ({
documentTemplateModel: { modelName: 'DocumentTemplate' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listDocumentTemplatesRouteHandler,
getDocumentTemplateRouteHandler,
newDocumentTemplateRouteHandler,
editDocumentTemplateRouteHandler,
} = await import('../documenttemplates.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { documentTemplateModel } = await import(
'../../../database/schemas/management/documenttemplate.schema.js'
);
describe('Document Template Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listDocumentTemplatesRouteHandler', () => {
it('should list document templates', async () => {
const mockResult = [{ _id: '1', name: 'Invoice Template' }];
listObjects.mockResolvedValue(mockResult);
await listDocumentTemplatesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: documentTemplateModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newDocumentTemplateRouteHandler', () => {
it('should create a new document template', async () => {
req.body = { name: 'New Template', documentSize: 'size123' };
const mockTemplate = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockTemplate);
await newDocumentTemplateRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockTemplate);
});
});
});

View File

@ -0,0 +1,98 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/filament.schema.js', () => ({
filamentModel: { modelName: 'Filament' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listFilamentsRouteHandler,
getFilamentRouteHandler,
newFilamentRouteHandler,
editFilamentRouteHandler,
} = await import('../filaments.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { filamentModel } = await import('../../../database/schemas/management/filament.schema.js');
describe('Filament Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listFilamentsRouteHandler', () => {
it('should list filaments', async () => {
const mockResult = [{ _id: '1', name: 'Filament 1' }];
listObjects.mockResolvedValue(mockResult);
await listFilamentsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: filamentModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newFilamentRouteHandler', () => {
it('should create a new filament', async () => {
req.body = { name: 'PLA Red', diameter: 1.75, cost: 20 };
const mockFilament = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockFilament);
await newFilamentRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockFilament);
});
});
describe('editFilamentRouteHandler', () => {
it('should update a filament', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Filament', cost: 25 };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editFilamentRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,123 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
flushFile: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/file.schema.js', () => ({
fileModel: { modelName: 'File' },
}));
jest.unstable_mockModule('../../../database/ceph.js', () => ({
uploadFile: jest.fn(),
downloadFile: jest.fn(),
deleteFile: jest.fn(),
BUCKETS: { FILES: 'test-bucket' },
}));
jest.unstable_mockModule('../../../utils.js', () => ({
getFileMeta: jest.fn(),
}));
jest.unstable_mockModule('multer', () => {
const mockMemoryStorage = jest.fn();
const mockMulter = jest.fn(() => ({
single: jest.fn(),
}));
mockMulter.memoryStorage = mockMemoryStorage;
return {
default: mockMulter,
memoryStorage: mockMemoryStorage,
};
});
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listFilesRouteHandler,
getFileRouteHandler,
editFileRouteHandler,
flushFileRouteHandler,
} = await import('../files.js');
const { listObjects, getObject, editObject, flushFile } = await import(
'../../../database/database.js'
);
const { fileModel } = await import('../../../database/schemas/management/file.schema.js');
describe('File Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listFilesRouteHandler', () => {
it('should list files', async () => {
const mockResult = [{ _id: '1', name: 'file.pdf' }];
listObjects.mockResolvedValue(mockResult);
await listFilesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: fileModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getFileRouteHandler', () => {
it('should get a file by ID', async () => {
req.params.id = '123';
const mockFile = { _id: '123', name: 'test.pdf' };
getObject.mockResolvedValue(mockFile);
await getFileRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }));
expect(res.send).toHaveBeenCalledWith(mockFile);
});
});
describe('flushFileRouteHandler', () => {
it('should flush/delete a file', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockResult = { success: true };
flushFile.mockResolvedValue(mockResult);
await flushFileRouteHandler(req, res);
expect(flushFile).toHaveBeenCalledWith(
expect.objectContaining({ id: '507f1f77bcf86cd799439011' })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,99 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/host.schema.js', () => ({
hostModel: { modelName: 'Host' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listHostsRouteHandler,
getHostRouteHandler,
newHostRouteHandler,
editHostRouteHandler,
deleteHostRouteHandler,
} = await import('../hosts.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { hostModel } = await import('../../../database/schemas/management/host.schema.js');
describe('Host Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listHostsRouteHandler', () => {
it('should list hosts', async () => {
const mockResult = [{ _id: '1', name: 'Host 1' }];
listObjects.mockResolvedValue(mockResult);
await listHostsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: hostModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newHostRouteHandler', () => {
it('should create a new host', async () => {
req.body = { name: 'New Host', address: '192.168.1.100' };
const mockHost = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockHost);
await newHostRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockHost);
});
});
describe('editHostRouteHandler', () => {
it('should update a host', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Host' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editHostRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,75 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/schemas/management/material.schema.js', () => ({
materialModel: {
modelName: 'Material',
aggregate: jest.fn(),
findOne: jest.fn(),
updateOne: jest.fn(),
create: jest.fn(),
},
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listMaterialsRouteHandler,
getMaterialRouteHandler,
newMaterialRouteHandler,
} = await import('../materials.js');
const { materialModel } = await import('../../../database/schemas/management/material.schema.js');
describe('Material Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listMaterialsRouteHandler', () => {
it('should list materials', async () => {
const mockResult = [{ name: 'PLA' }];
materialModel.aggregate.mockResolvedValue(mockResult);
await listMaterialsRouteHandler(req, res);
expect(materialModel.aggregate).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getMaterialRouteHandler', () => {
it('should get a material by ID', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockMaterial = { _id: '507f1f77bcf86cd799439011', name: 'PLA' };
materialModel.findOne.mockResolvedValue(mockMaterial);
await getMaterialRouteHandler(req, res);
expect(materialModel.findOne).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockMaterial);
});
});
});

View File

@ -0,0 +1,86 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/notetype.schema.js', () => ({
noteTypeModel: { modelName: 'NoteType' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listNoteTypesRouteHandler,
getNoteTypeRouteHandler,
newNoteTypeRouteHandler,
editNoteTypeRouteHandler,
} = await import('../notetypes.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { noteTypeModel } = await import('../../../database/schemas/management/notetype.schema.js');
describe('Note Type Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listNoteTypesRouteHandler', () => {
it('should list note types', async () => {
const mockResult = [{ _id: '1', name: 'General' }];
listObjects.mockResolvedValue(mockResult);
await listNoteTypesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: noteTypeModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newNoteTypeRouteHandler', () => {
it('should create a new note type', async () => {
req.body = { name: 'Important', color: '#ff0000' };
const mockNoteType = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockNoteType);
await newNoteTypeRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockNoteType);
});
});
});

View File

@ -0,0 +1,124 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/part.schema.js', () => ({
partModel: { modelName: 'Part' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listPartsRouteHandler,
getPartRouteHandler,
newPartRouteHandler,
editPartRouteHandler,
deletePartRouteHandler,
} = await import('../parts.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { partModel } = await import('../../../database/schemas/management/part.schema.js');
describe('Part Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listPartsRouteHandler', () => {
it('should list parts', async () => {
const mockResult = [{ _id: '1', name: 'Part 1' }];
listObjects.mockResolvedValue(mockResult);
await listPartsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: partModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getPartRouteHandler', () => {
it('should get a part by ID', async () => {
req.params.id = '123';
const mockPart = { _id: '123', name: 'Test Part' };
getObject.mockResolvedValue(mockPart);
await getPartRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }));
expect(res.send).toHaveBeenCalledWith(mockPart);
});
});
describe('newPartRouteHandler', () => {
it('should create a new part', async () => {
req.body = { name: 'New Part', price: 10.99 };
const mockPart = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockPart);
await newPartRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockPart);
});
});
describe('editPartRouteHandler', () => {
it('should update a part', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Part', price: 15.99 };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editPartRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('deletePartRouteHandler', () => {
it('should delete a part', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockResult = { _id: '507f1f77bcf86cd799439011' };
deleteObject.mockResolvedValue(mockResult);
await deletePartRouteHandler(req, res);
expect(deleteObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,98 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/product.schema.js', () => ({
productModel: { modelName: 'Product' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listProductsRouteHandler,
getProductRouteHandler,
newProductRouteHandler,
editProductRouteHandler,
deleteProductRouteHandler,
} = await import('../products.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { productModel } = await import('../../../database/schemas/management/product.schema.js');
describe('Product Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listProductsRouteHandler', () => {
it('should list products', async () => {
const mockResult = [{ _id: '1', name: 'Product 1' }];
listObjects.mockResolvedValue(mockResult);
await listProductsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: productModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newProductRouteHandler', () => {
it('should create a new product', async () => {
req.body = { name: 'New Product', parts: [] };
const mockProduct = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockProduct);
await newProductRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockProduct);
});
});
describe('editProductRouteHandler', () => {
it('should update a product', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Product' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editProductRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,98 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/taxrate.schema.js', () => ({
taxRateModel: { modelName: 'TaxRate' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listTaxRatesRouteHandler,
getTaxRateRouteHandler,
newTaxRateRouteHandler,
editTaxRateRouteHandler,
} = await import('../taxrates.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { taxRateModel } = await import('../../../database/schemas/management/taxrate.schema.js');
describe('Tax Rate Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listTaxRatesRouteHandler', () => {
it('should list tax rates', async () => {
const mockResult = [{ _id: '1', name: 'GST', rate: 10 }];
listObjects.mockResolvedValue(mockResult);
await listTaxRatesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: taxRateModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newTaxRateRouteHandler', () => {
it('should create a new tax rate', async () => {
req.body = { name: 'VAT', rate: 20, rateType: 'percentage' };
const mockTaxRate = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockTaxRate);
await newTaxRateRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockTaxRate);
});
});
describe('editTaxRateRouteHandler', () => {
it('should update a tax rate', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { rate: 15 };
const mockResult = { _id: '507f1f77bcf86cd799439011', rate: 15 };
editObject.mockResolvedValue(mockResult);
await editTaxRateRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,86 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/taxrecord.schema.js', () => ({
taxRecordModel: { modelName: 'TaxRecord' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listTaxRecordsRouteHandler,
getTaxRecordRouteHandler,
newTaxRecordRouteHandler,
editTaxRecordRouteHandler,
} = await import('../taxrecords.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { taxRecordModel } = await import('../../../database/schemas/management/taxrecord.schema.js');
describe('Tax Record Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listTaxRecordsRouteHandler', () => {
it('should list tax records', async () => {
const mockResult = [{ _id: '1', amount: 100, taxAmount: 10 }];
listObjects.mockResolvedValue(mockResult);
await listTaxRecordsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: taxRecordModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newTaxRecordRouteHandler', () => {
it('should create a new tax record', async () => {
req.body = { taxRate: 'rate123', amount: 100, taxAmount: 10 };
const mockRecord = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockRecord);
await newTaxRecordRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockRecord);
});
});
});

View File

@ -0,0 +1,126 @@
import { jest } from '@jest/globals';
// Mock dependencies MUST be done before importing the module under test
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
listObjectsByProperties: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/user.schema.js', () => ({
userModel: { findOne: jest.fn() },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
// Now import the modules
const { listUsersRouteHandler, getUserRouteHandler, editUserRouteHandler } = await import(
'../users.js'
);
const { listObjects, getObject, editObject } = await import('../../../database/database.js');
const { userModel } = await import('../../../database/schemas/management/user.schema.js');
describe('User Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listUsersRouteHandler', () => {
it('should list users and send response', async () => {
const mockResult = [{ id: '1', name: 'User 1' }];
listObjects.mockResolvedValue(mockResult);
await listUsersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({
model: userModel,
})
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
it('should handle errors from listObjects', async () => {
const mockError = { error: 'Database error', code: 500 };
listObjects.mockResolvedValue(mockError);
await listUsersRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.send).toHaveBeenCalledWith(mockError);
});
});
describe('getUserRouteHandler', () => {
it('should get a user by id and send response', async () => {
req.params.id = '123';
const mockUser = { id: '123', name: 'Test User' };
getObject.mockResolvedValue(mockUser);
await getUserRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(
expect.objectContaining({
model: userModel,
id: '123',
})
);
expect(res.send).toHaveBeenCalledWith(mockUser);
});
it('should handle user not found', async () => {
req.params.id = 'invalid';
const mockError = { error: 'Not found', code: 404 };
getObject.mockResolvedValue(mockError);
await getUserRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(404);
expect(res.send).toHaveBeenCalledWith(mockError);
});
});
describe('editUserRouteHandler', () => {
it('should edit a user and send response', async () => {
req.params.id = '507f1f77bcf86cd799439011'; // valid mongodb id format
req.body = { name: 'New Name' };
const mockResult = { id: '507f1f77bcf86cd799439011', name: 'New Name' };
editObject.mockResolvedValue(mockResult);
await editUserRouteHandler(req, res);
expect(editObject).toHaveBeenCalledWith(
expect.objectContaining({
model: userModel,
updateData: expect.objectContaining({ name: 'New Name' }),
})
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,99 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/management/vendor.schema.js', () => ({
vendorModel: { modelName: 'Vendor' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listVendorsRouteHandler,
getVendorRouteHandler,
newVendorRouteHandler,
editVendorRouteHandler,
deleteVendorRouteHandler,
} = await import('../vendors.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { vendorModel } = await import('../../../database/schemas/management/vendor.schema.js');
describe('Vendor Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listVendorsRouteHandler', () => {
it('should list vendors', async () => {
const mockResult = [{ _id: '1', name: 'Vendor 1' }];
listObjects.mockResolvedValue(mockResult);
await listVendorsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: vendorModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newVendorRouteHandler', () => {
it('should create a new vendor', async () => {
req.body = { name: 'New Vendor', email: 'vendor@example.com' };
const mockVendor = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockVendor);
await newVendorRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockVendor);
});
});
describe('editVendorRouteHandler', () => {
it('should update a vendor', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Vendor' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editVendorRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,85 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
recursivelyDeleteChildObjects: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/misc/note.schema.js', () => ({
noteModel: { modelName: 'Note' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listNotesRouteHandler,
getNoteRouteHandler,
newNoteRouteHandler,
editNoteRouteHandler,
} = await import('../../misc/notes.js');
const { listObjects, getObject, editObject, newObject } = await import(
'../../../database/database.js'
);
const { noteModel } = await import('../../../database/schemas/misc/note.schema.js');
describe('Note Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listNotesRouteHandler', () => {
it('should list notes', async () => {
const mockResult = [{ _id: '1', content: 'Test note' }];
listObjects.mockResolvedValue(mockResult);
await listNotesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: noteModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newNoteRouteHandler', () => {
it('should create a new note', async () => {
req.body = { content: 'New note', noteType: 'type123' };
const mockNote = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockNote);
await newNoteRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockNote);
});
});
});

View File

@ -0,0 +1,106 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/production/gcodefile.schema.js', () => ({
gcodeFileModel: {
modelName: 'GCodeFile',
aggregate: jest.fn(),
},
}));
jest.unstable_mockModule('../../management/files.js', () => ({
getFileContentRouteHandler: jest.fn(),
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listGCodeFilesRouteHandler,
getGCodeFileRouteHandler,
newGCodeFileRouteHandler,
editGCodeFileRouteHandler,
deleteGCodeFileRouteHandler,
} = await import('../gcodefiles.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { gcodeFileModel } = await import('../../../database/schemas/production/gcodefile.schema.js');
describe('GCodeFile Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listGCodeFilesRouteHandler', () => {
it('should list gcode files', async () => {
const mockResult = [{ _id: '1', name: 'file.gcode' }];
listObjects.mockResolvedValue(mockResult);
await listGCodeFilesRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(
expect.objectContaining({ model: gcodeFileModel })
);
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newGCodeFileRouteHandler', () => {
it('should create a new gcode file', async () => {
req.body = { name: 'newfile.gcode', file: 'file123' };
const mockFile = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockFile);
await newGCodeFileRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockFile);
});
});
describe('editGCodeFileRouteHandler', () => {
it('should update a gcode file', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'updated.gcode' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editGCodeFileRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,114 @@
import { jest } from '@jest/globals';
// Mock dependencies
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/production/job.schema.js', () => ({
jobModel: { modelName: 'Job' },
}));
jest.unstable_mockModule('../../../database/schemas/production/subjob.schema.js', () => ({
subJobModel: { modelName: 'SubJob' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
// Import handlers after mocking
const { listJobsRouteHandler, getJobRouteHandler, newJobRouteHandler, deleteJobRouteHandler } =
await import('../jobs.js');
const { listObjects, getObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { jobModel } = await import('../../../database/schemas/production/job.schema.js');
const { subJobModel } = await import('../../../database/schemas/production/subjob.schema.js');
describe('Job Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listJobsRouteHandler', () => {
it('should list jobs', async () => {
const mockResult = [{ _id: '1', name: 'Job 1' }];
listObjects.mockResolvedValue(mockResult);
await listJobsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newJobRouteHandler', () => {
it('should create a new job and corresponding subjobs', async () => {
req.body = {
quantity: 2,
printers: ['p1', 'p2'],
gcodeFile: 'file123',
};
const mockJob = { _id: 'job123' };
newObject.mockResolvedValueOnce(mockJob); // For Job
newObject.mockResolvedValue({ _id: 'subjob' }); // For SubJobs
await newJobRouteHandler(req, res);
expect(newObject).toHaveBeenCalledTimes(3); // 1 Job + 2 SubJobs
expect(res.send).toHaveBeenCalledWith(mockJob);
});
it('should handle errors during job creation', async () => {
req.body = { quantity: 1, printers: ['p1'] };
newObject.mockResolvedValueOnce({ error: 'Failed', code: 500 });
await newJobRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(500);
expect(res.send).toHaveBeenCalledWith(expect.objectContaining({ error: 'Failed' }));
});
});
describe('deleteJobRouteHandler', () => {
it('should delete a job', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockResult = { _id: '507f1f77bcf86cd799439011' };
deleteObject.mockResolvedValue(mockResult);
await deleteJobRouteHandler(req, res);
expect(deleteObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,99 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/production/printer.schema.js', () => ({
printerModel: { modelName: 'Printer' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listPrintersRouteHandler,
getPrinterRouteHandler,
newPrinterRouteHandler,
editPrinterRouteHandler,
deletePrinterRouteHandler,
} = await import('../printers.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { printerModel } = await import('../../../database/schemas/production/printer.schema.js');
describe('Printer Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listPrintersRouteHandler', () => {
it('should list printers', async () => {
const mockResult = [{ _id: '1', name: 'Printer 1' }];
listObjects.mockResolvedValue(mockResult);
await listPrintersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: printerModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newPrinterRouteHandler', () => {
it('should create a new printer', async () => {
req.body = { name: 'New Printer', host: 'host123' };
const mockPrinter = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockPrinter);
await newPrinterRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockPrinter);
});
});
describe('editPrinterRouteHandler', () => {
it('should update a printer', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Printer' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editPrinterRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,74 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/production/subjob.schema.js', () => ({
subJobModel: { modelName: 'SubJob' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const { listSubJobsRouteHandler, getSubJobRouteHandler } = await import('../subjobs.js');
const { listObjects, getObject } = await import('../../../database/database.js');
const { subJobModel } = await import('../../../database/schemas/production/subjob.schema.js');
describe('SubJob Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listSubJobsRouteHandler', () => {
it('should list sub jobs', async () => {
const mockResult = [{ _id: '1', number: 1 }];
listObjects.mockResolvedValue(mockResult);
await listSubJobsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: subJobModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('getSubJobRouteHandler', () => {
it('should get a sub job by ID', async () => {
req.params.id = '123';
const mockSubJob = { _id: '123', number: 1 };
getObject.mockResolvedValue(mockSubJob);
await getSubJobRouteHandler(req, res);
expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }));
expect(res.send).toHaveBeenCalledWith(mockSubJob);
});
});
});

View File

@ -0,0 +1,112 @@
import { jest } from '@jest/globals';
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/sales/client.schema.js', () => ({
clientModel: { modelName: 'Client' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
const {
listClientsRouteHandler,
getClientRouteHandler,
newClientRouteHandler,
editClientRouteHandler,
deleteClientRouteHandler,
} = await import('../clients.js');
const { listObjects, getObject, editObject, newObject, deleteObject } = await import(
'../../../database/database.js'
);
const { clientModel } = await import('../../../database/schemas/sales/client.schema.js');
describe('Client Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listClientsRouteHandler', () => {
it('should list clients', async () => {
const mockResult = [{ _id: '1', name: 'Client 1' }];
listObjects.mockResolvedValue(mockResult);
await listClientsRouteHandler(req, res);
expect(listObjects).toHaveBeenCalledWith(expect.objectContaining({ model: clientModel }));
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('newClientRouteHandler', () => {
it('should create a new client', async () => {
req.body = { name: 'New Client', email: 'client@example.com' };
const mockClient = { _id: '456', ...req.body };
newObject.mockResolvedValue(mockClient);
await newClientRouteHandler(req, res);
expect(newObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockClient);
});
});
describe('editClientRouteHandler', () => {
it('should update a client', async () => {
req.params.id = '507f1f77bcf86cd799439011';
req.body = { name: 'Updated Client' };
const mockResult = { _id: '507f1f77bcf86cd799439011', ...req.body };
editObject.mockResolvedValue(mockResult);
await editClientRouteHandler(req, res);
expect(editObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('deleteClientRouteHandler', () => {
it('should delete a client', async () => {
req.params.id = '507f1f77bcf86cd799439011';
const mockResult = { _id: '507f1f77bcf86cd799439011' };
deleteObject.mockResolvedValue(mockResult);
await deleteClientRouteHandler(req, res);
expect(deleteObject).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
});

View File

@ -0,0 +1,123 @@
import { jest } from '@jest/globals';
// Mock dependencies
jest.unstable_mockModule('../../../database/database.js', () => ({
listObjects: jest.fn(),
getObject: jest.fn(),
editObject: jest.fn(),
editObjects: jest.fn(),
newObject: jest.fn(),
deleteObject: jest.fn(),
listObjectsByProperties: jest.fn(),
getModelStats: jest.fn(),
getModelHistory: jest.fn(),
checkStates: jest.fn(),
}));
jest.unstable_mockModule('../../../database/schemas/sales/salesorder.schema.js', () => ({
salesOrderModel: { modelName: 'SalesOrder' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({
orderItemModel: { modelName: 'OrderItem' },
}));
jest.unstable_mockModule('../../../database/schemas/inventory/shipment.schema.js', () => ({
shipmentModel: { modelName: 'Shipment' },
}));
jest.unstable_mockModule('log4js', () => ({
default: {
getLogger: () => ({
level: 'info',
debug: jest.fn(),
error: jest.fn(),
warn: jest.fn(),
trace: jest.fn(),
}),
},
}));
// Import handlers after mocking
const {
listSalesOrdersRouteHandler,
getSalesOrderRouteHandler,
newSalesOrderRouteHandler,
postSalesOrderRouteHandler,
} = await import('../salesorders.js');
const { listObjects, getObject, editObject, newObject, checkStates } = await import(
'../../../database/database.js'
);
const { salesOrderModel } = await import('../../../database/schemas/sales/salesorder.schema.js');
describe('Sales Order Service Route Handlers', () => {
let req, res;
beforeEach(() => {
req = {
params: {},
query: {},
body: {},
user: { id: 'test-user-id' },
};
res = {
send: jest.fn(),
status: jest.fn().mockReturnThis(),
};
jest.clearAllMocks();
});
describe('listSalesOrdersRouteHandler', () => {
it('should list sales orders', async () => {
const mockResult = [{ _id: '1', reference: 'SO-001' }];
listObjects.mockResolvedValue(mockResult);
await listSalesOrdersRouteHandler(req, res);
expect(listObjects).toHaveBeenCalled();
expect(res.send).toHaveBeenCalledWith(mockResult);
});
});
describe('postSalesOrderRouteHandler', () => {
it('should post a draft sales order and update items/shipments', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
// Mock listObjects for orderItems and shipments
listObjects.mockResolvedValueOnce([
{ _id: 'oi1', state: { type: 'draft' }, shipment: 's1', _reference: 'ITEM1' },
]); // orderItems
listObjects.mockResolvedValueOnce([
{ _id: 's1', state: { type: 'draft' }, _reference: 'SHIP1' },
]); // shipments
editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'sent' } });
await postSalesOrderRouteHandler(req, res);
expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['draft'] }));
expect(editObject).toHaveBeenCalledTimes(3); // 1 OrderItem + 1 Shipment + 1 SalesOrder
expect(res.send).toHaveBeenCalled();
});
it('should fail if an order item is not in draft state', async () => {
req.params.id = '507f1f77bcf86cd799439011';
checkStates.mockResolvedValue(true);
listObjects.mockResolvedValueOnce([
{ _id: 'oi1', state: { type: 'ordered' }, _reference: 'ITEM1' },
]);
listObjects.mockResolvedValueOnce([]);
await postSalesOrderRouteHandler(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({ error: 'Order item ITEM1 not in draft state.' })
);
});
});
});

1655
yarn.lock

File diff suppressed because it is too large Load Diff