plugin-financials/__tests__/routes/accounts.test.js
2025-11-03 13:51:33 +02:00

347 lines
10 KiB
JavaScript

/**
* Chart of Accounts Route Tests
*
* Tests for `/api/plugins/financials/accounts` endpoints
* Uses REAL PostgreSQL database - NO mocks
*/
const request = require('supertest');
const express = require('express');
const accountsRouter = require('../../routes/accounts');
const FinancialTestHelper = require('../helpers/testHelper');
describe('Chart of Accounts Routes', () => {
let app;
let testHelper;
let testSite;
let testUserId;
let chartOfAccountsRepository;
beforeAll(async () => {
// Create Express app for testing
app = express();
app.use(express.json());
app.use('/api/plugins/financials', accountsRouter);
// Initialize test helper
testHelper = new FinancialTestHelper();
// Create test data
testSite = await testHelper.createTestSite();
testUserId = testHelper.userId;
// Import repository
chartOfAccountsRepository = require('../../repositories').chartOfAccountsRepository;
});
afterAll(async () => {
// Cleanup handled by global schema manager
});
describe('GET /api/plugins/financials/accounts', () => {
it('should return all accounts for a site', async () => {
// Create test accounts
const accountsData = [
{
site_id: testSite.id,
account_code: '1000',
account_name: 'Cash Account',
account_type: 'asset',
description: 'Cash and cash equivalents',
is_active: true,
created_by: testUserId
},
{
site_id: testSite.id,
account_code: '2000',
account_name: 'Accounts Payable',
account_type: 'liability',
description: 'Amounts owed to vendors',
is_active: true,
created_by: testUserId
}
];
// Insert accounts using repository
for (const account of accountsData) {
await chartOfAccountsRepository.create(account);
}
const response = await request(app)
.get('/api/plugins/financials/accounts')
.query({ site_id: testSite.id });
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toBeInstanceOf(Array);
expect(response.body.data.length).toBeGreaterThanOrEqual(2);
// Verify account structure matches expected format
const account = response.body.data.find(a => a.account_code === '1000');
expect(account).toBeDefined();
expect(account.account_name).toBe('Cash Account');
expect(account.account_type).toBe('asset');
});
it('should return empty array if no accounts exist', async () => {
// Create a different site with no accounts
const anotherSite = await testHelper.createTestSite();
const response = await request(app)
.get('/api/plugins/financials/accounts')
.query({ site_id: anotherSite.id });
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toEqual([]);
});
it('should filter accounts by active status', async () => {
// Create active and inactive accounts
await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '3000',
account_name: 'Active Account',
account_type: 'asset',
is_active: true,
created_by: testUserId
});
await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '4000',
account_name: 'Inactive Account',
account_type: 'asset',
is_active: false,
created_by: testUserId
});
const response = await request(app)
.get('/api/plugins/financials/accounts')
.query({ site_id: testSite.id });
expect(response.status).toBe(200);
// Should include both active and inactive (implementation detail)
expect(response.body.data.length).toBeGreaterThan(0);
});
});
describe('GET /api/plugins/financials/accounts/:id', () => {
let testAccount;
beforeEach(async () => {
// Create a test account
testAccount = await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '5000',
account_name: 'Test Account',
account_type: 'equity',
description: 'Test account for detailed view',
is_active: true,
created_by: testUserId
});
});
it('should return account details by ID', async () => {
const response = await request(app)
.get(`/api/plugins/financials/accounts/${testAccount.id}`)
.query({ site_id: testSite.id });
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toBeDefined();
expect(response.body.data.id).toBe(testAccount.id);
expect(response.body.data.account_code).toBe('5000');
expect(response.body.data.account_name).toBe('Test Account');
});
it('should return 404 for non-existent account', async () => {
const fakeId = '00000000-0000-0000-0000-000000000000';
const response = await request(app)
.get(`/api/plugins/financials/accounts/${fakeId}`)
.query({ site_id: testSite.id });
expect(response.status).toBe(404);
});
});
describe('POST /api/plugins/financials/accounts', () => {
it('should create a new account', async () => {
const newAccount = {
site_id: testSite.id,
account_code: '6000',
account_name: 'New Test Account',
account_type: 'expense',
description: 'Test expense account',
is_active: true
};
const response = await request(app)
.post('/api/plugins/financials/accounts')
.send(newAccount);
expect(response.status).toBe(201);
expect(response.body.success).toBe(true);
expect(response.body.data).toBeDefined();
expect(response.body.data.account_code).toBe('6000');
expect(response.body.data.account_name).toBe('New Test Account');
});
it('should reject duplicate account codes for same site', async () => {
const account1 = {
site_id: testSite.id,
account_code: '7000',
account_name: 'First Account',
account_type: 'asset',
is_active: true
};
await request(app)
.post('/api/plugins/financials/accounts')
.send(account1);
// Try to create another with same code
const account2 = {
site_id: testSite.id,
account_code: '7000',
account_name: 'Duplicate Account',
account_type: 'asset',
is_active: true
};
const response = await request(app)
.post('/api/plugins/financials/accounts')
.send(account2);
// Should fail validation
expect(response.status).toBeGreaterThanOrEqual(400);
});
it('should require all required fields', async () => {
const incompleteAccount = {
site_id: testSite.id,
account_name: 'Incomplete Account'
// Missing account_code and account_type
};
const response = await request(app)
.post('/api/plugins/financials/accounts')
.send(incompleteAccount);
expect(response.status).toBe(400);
});
});
describe('PUT /api/plugins/financials/accounts/:id', () => {
let testAccount;
beforeEach(async () => {
testAccount = await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '8000',
account_name: 'Original Name',
account_type: 'revenue',
description: 'Original description',
is_active: true,
created_by: testUserId
});
});
it('should update account details', async () => {
const updateData = {
account_name: 'Updated Name',
description: 'Updated description',
is_active: false
};
const response = await request(app)
.put(`/api/plugins/financials/accounts/${testAccount.id}`)
.send(updateData);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data.account_name).toBe('Updated Name');
expect(response.body.data.description).toBe('Updated description');
expect(response.body.data.is_active).toBe(false);
});
it('should return 404 for non-existent account', async () => {
const fakeId = '00000000-0000-0000-0000-000000000000';
const response = await request(app)
.put(`/api/plugins/financials/accounts/${fakeId}`)
.send({ account_name: 'Test' });
expect(response.status).toBe(404);
});
});
describe('DELETE /api/plugins/financials/accounts/:id', () => {
let testAccount;
beforeEach(async () => {
testAccount = await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '9000',
account_name: 'Account to Delete',
account_type: 'asset',
is_active: true,
created_by: testUserId
});
});
it('should delete an account', async () => {
const response = await request(app)
.delete(`/api/plugins/financials/accounts/${testAccount.id}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
// Verify account is deleted
const deletedAccount = await chartOfAccountsRepository.findById(testAccount.id);
expect(deletedAccount).toBeUndefined();
});
it('should return 404 for non-existent account', async () => {
const fakeId = '00000000-0000-0000-0000-000000000000';
const response = await request(app)
.delete(`/api/plugins/financials/accounts/${fakeId}`);
expect(response.status).toBe(404);
});
});
describe('GET /api/plugins/financials/accounts/:id/balance', () => {
let testAccount;
beforeEach(async () => {
testAccount = await chartOfAccountsRepository.create({
site_id: testSite.id,
account_code: '10000',
account_name: 'Balance Test Account',
account_type: 'asset',
is_active: true,
created_by: testUserId
});
});
it('should return account balance calculation', async () => {
const response = await request(app)
.get(`/api/plugins/financials/accounts/${testAccount.id}/balance`)
.query({
site_id: testSite.id,
start_date: '2024-01-01',
end_date: '2024-12-31'
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toBeDefined();
expect(response.body.data.account_id).toBeDefined();
// Balance calculation structure should exist
// (exact structure depends on implementation)
});
});
});