@accounter/server
Version:
Accounter GraphQL server
117 lines • 5.03 kB
JavaScript
/**
* 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';
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 = [];
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(`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 = {
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();
//# sourceMappingURL=validate-demo-data.js.map