UNPKG

@accounter/server

Version:
154 lines (130 loc) 4.85 kB
/** * Demo Data Validation Script * * Validates the integrity of seeded demo data with comprehensive ledger validation. * * Validation Checks: * - Admin business entity exists * - Charge count matches expected * - Ledger records for each use-case: * - Per-record internal balance (FR1) * - Aggregate balance (FR2) * - Entity-level balance (FR3) * - No orphaned amounts (FR4) * - Positive amounts only (FR5) * - Foreign currency consistency (FR6) * - Valid dates (FR7) * - Record count matches (FR8) * * Exit codes: * - 0: All validations passed * - 1: Validation errors found or database connection failed */ import { config } from 'dotenv'; import pg from 'pg'; import { getAllUseCases } from './use-cases/index.js'; import { validateLedgerRecords } from './validators/ledger-validators.js'; import type { LedgerRecord, ValidationContext } from './validators/types.js'; config({ path: [`.env`, `../../.env`], }); const DEFAULT_CURRENCY = 'ILS'; const BALANCE_TOLERANCE = 0.005; /** * Main validation function - connects to DB and runs all checks */ async function validateDemoData() { const client = new pg.Client({ user: process.env.POSTGRES_USER, password: process.env.POSTGRES_PASSWORD, host: process.env.POSTGRES_HOST, port: parseInt(process.env.POSTGRES_PORT || '5432'), database: process.env.POSTGRES_DB, ssl: process.env.POSTGRES_SSL === '1', }); const errors: string[] = []; try { await client.connect(); // 1. Admin business exists const adminCheck = await client.query( `SELECT id FROM accounter_schema.financial_entities WHERE type = 'business' AND name = 'Admin Business'`, ); if (adminCheck.rows.length === 0) { errors.push('Admin business entity missing'); } // 2. Use-case charge count reconciliation const useCases = getAllUseCases(); const expectedChargeCount = useCases.reduce((sum, uc) => sum + uc.fixtures.charges.length, 0); const actualChargeCount = await client.query(`SELECT COUNT(*) FROM accounter_schema.charges`); if (parseInt(actualChargeCount.rows[0].count) !== expectedChargeCount) { errors.push( `Charge count mismatch: expected ${expectedChargeCount}, got ${actualChargeCount.rows[0].count}`, ); } // 3. Comprehensive ledger validation for all use-cases with expectations (FR9) const useCasesWithExpectations = useCases.filter(uc => uc.expectations); console.log( `\nValidating ledger records for ${useCasesWithExpectations.length} use-case(s)...`, ); let ledgerRecordsExist = false; for (const useCase of useCasesWithExpectations) { // Get all charges for this use-case const chargeIds = useCase.fixtures.charges.map(c => c.id); // Fetch all ledger records for these charges const ledgerRecords = await client.query<LedgerRecord>( `SELECT * FROM accounter_schema.ledger_records WHERE charge_id = ANY($1) ORDER BY created_at`, [chargeIds], ); if (ledgerRecords.rows.length === 0) { // Ledger generation is separate from seeding - this is expected console.log(` ⚠ ${useCase.id}: no ledger records (ledger generation not run)`); continue; } ledgerRecordsExist = true; // Create validation context const context: ValidationContext = { useCaseId: useCase.id, defaultCurrency: DEFAULT_CURRENCY, tolerance: BALANCE_TOLERANCE, }; // Run comprehensive validation const validationErrors = validateLedgerRecords( ledgerRecords.rows, useCase.expectations!.ledgerRecordCount, context, ); errors.push(...validationErrors); // Log progress if (validationErrors.length === 0) { console.log(` ✓ ${useCase.id} (${ledgerRecords.rows.length} records)`); } else { console.log(` ✗ ${useCase.id} (${validationErrors.length} error(s))`); } } if (!ledgerRecordsExist) { console.log( ' ℹ️ Ledger records not found. Run ledger generation to create and validate ledger entries.', ); } // 4. VAT row present (percentage stored as decimal 0.17 for 17%) const vatCheck = await client.query( `SELECT 1 FROM accounter_schema.vat_value WHERE percentage = 0.17`, ); if (vatCheck.rows.length === 0) { errors.push('VAT default (17%) missing'); } // Report errors or success if (errors.length > 0) { console.error('❌ Validation failed:'); for (const err of errors) console.error(` - ${err}`); process.exit(1); } console.log('✅ Demo data validation passed'); } catch (error) { console.error('❌ Validation error:', error); process.exit(1); } finally { await client.end(); } } validateDemoData();