/** * 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')); }); });