bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
105 lines
3.6 KiB
JavaScript
105 lines
3.6 KiB
JavaScript
/**
|
|
* test/period-close.test.mjs — Period closing validation tests
|
|
*/
|
|
import { describe, it } from 'node:test';
|
|
import assert from 'node:assert';
|
|
|
|
// Mock pool that simulates period states
|
|
function createMockPool(periodState = 'open') {
|
|
return {
|
|
query: async (sql, params) => {
|
|
if (sql.includes('SELECT status FROM ledger_periods')) {
|
|
return { rows: [{ status: periodState }] };
|
|
}
|
|
if (sql.includes('INSERT INTO ledger_journal_entries')) {
|
|
return { rows: [{ id: 'test-id', status: 'draft' }] };
|
|
}
|
|
return { rows: [] };
|
|
},
|
|
connect: async () => ({
|
|
query: async (sql, params) => {
|
|
if (sql === 'BEGIN' || sql === 'COMMIT' || sql === 'ROLLBACK') return {};
|
|
if (sql.includes('SELECT status FROM ledger_periods')) {
|
|
return { rows: [{ status: periodState }] };
|
|
}
|
|
if (sql.includes('INSERT INTO ledger_journal_entries')) {
|
|
return { rows: [{ id: 'test-id', status: 'draft' }] };
|
|
}
|
|
return { rows: [] };
|
|
},
|
|
release: () => {}
|
|
})
|
|
};
|
|
}
|
|
|
|
// Period validation logic (same as in routes)
|
|
async function validatePeriodOpen(pool, tenantId, period) {
|
|
const { rows } = await pool.query(
|
|
`SELECT status FROM ledger_periods WHERE tenant_id=$1 AND period=$2`,
|
|
[tenantId, period]
|
|
);
|
|
if (rows.length > 0 && rows[0].status === 'closed') {
|
|
return { allowed: false, status: 'closed', error: `Period ${period} är stängd` };
|
|
}
|
|
return { allowed: true, status: rows[0]?.status || 'open' };
|
|
}
|
|
|
|
describe('Period closing validation', () => {
|
|
it('should allow entries in open periods', async () => {
|
|
const pool = createMockPool('open');
|
|
const result = await validatePeriodOpen(pool, 'test-tenant', '2024-01');
|
|
assert.strictEqual(result.allowed, true);
|
|
assert.strictEqual(result.status, 'open');
|
|
});
|
|
|
|
it('should block entries in closed periods', async () => {
|
|
const pool = createMockPool('closed');
|
|
const result = await validatePeriodOpen(pool, 'test-tenant', '2024-01');
|
|
assert.strictEqual(result.allowed, false);
|
|
assert.strictEqual(result.status, 'closed');
|
|
assert.ok(result.error.includes('stängd'));
|
|
});
|
|
|
|
it('should allow entries when period does not exist yet', async () => {
|
|
const pool = {
|
|
query: async () => ({ rows: [] })
|
|
};
|
|
const result = await validatePeriodOpen(pool, 'test-tenant', '2024-01');
|
|
assert.strictEqual(result.allowed, true);
|
|
});
|
|
|
|
it('should handle period format correctly', async () => {
|
|
const validPeriods = ['2024-01', '2024-12', '2023-06'];
|
|
const invalidPeriods = ['01-2024', '2024', 'invalid', '24-01'];
|
|
|
|
for (const period of validPeriods) {
|
|
assert.match(period, /^\d{4}-\d{2}$/);
|
|
}
|
|
for (const period of invalidPeriods) {
|
|
assert.doesNotMatch(period, /^\d{4}-\d{2}$/);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Period workflow states', () => {
|
|
it('should track period state transitions', () => {
|
|
const states = ['open', 'review', 'approved', 'closed'];
|
|
const validTransitions = {
|
|
'open': ['review', 'closed'],
|
|
'review': ['approved', 'open'],
|
|
'approved': ['closed', 'open'],
|
|
'closed': ['open'] // reopen
|
|
};
|
|
|
|
// Test valid transitions
|
|
assert.ok(validTransitions['open'].includes('review'));
|
|
assert.ok(validTransitions['review'].includes('approved'));
|
|
assert.ok(validTransitions['approved'].includes('closed'));
|
|
assert.ok(validTransitions['closed'].includes('open'));
|
|
|
|
// Test invalid transitions
|
|
assert.ok(!validTransitions['open'].includes('approved'));
|
|
assert.ok(!validTransitions['closed'].includes('review'));
|
|
});
|
|
});
|