@accounter/server
Version:
Accounter GraphQL server
155 lines • 9.28 kB
JavaScript
import { describe, expect, it, beforeAll, afterAll } from 'vitest';
import { TestDatabase } from './helpers/db-setup.js';
import { seedAdminCore } from '../../scripts/seed-admin-context.js';
import { UUID_REGEX } from '../shared/constants.js';
describe('seedAdminCore integration', () => {
let db;
beforeAll(async () => {
db = new TestDatabase();
await db.connect();
});
afterAll(async () => {
// Do NOT close db here — the shared pool is managed by vitest-global-setup teardown.
// Closing it here destroys the pool for all other concurrently-running integration test suites.
});
it('should create admin business context with all required entities', async () => {
await db.withTransaction(async (client) => {
// Execute seed
const { adminEntityId } = await seedAdminCore(client);
// Verify admin entity exists
expect(adminEntityId).toBeTruthy();
expect(adminEntityId).toMatch(UUID_REGEX);
// Verify admin financial entity
const adminEntity = await client.query(`SELECT * FROM accounter_schema.financial_entities WHERE id = $1`, [adminEntityId]);
expect(adminEntity.rows).toHaveLength(1);
expect(adminEntity.rows[0].name).toBe('Admin Business');
expect(adminEntity.rows[0].type).toBe('business');
expect(adminEntity.rows[0].owner_id).toBe(adminEntityId); // self-owned
// Verify admin business record
const adminBusiness = await client.query(`SELECT * FROM accounter_schema.businesses WHERE id = $1`, [adminEntityId]);
expect(adminBusiness.rows).toHaveLength(1);
// Verify user_context exists and has required fields
const userContext = await client.query(`SELECT * FROM accounter_schema.user_context WHERE owner_id = $1`, [adminEntityId]);
expect(userContext.rows).toHaveLength(1);
const context = userContext.rows[0];
// Verify currencies
expect(context.default_local_currency).toBe('ILS');
expect(context.default_fiat_currency_for_crypto_conversions).toBe('USD');
// Verify business dates
expect(context.date_established).toBeTruthy();
expect(context.initial_accounter_year).toBe(2020);
// Verify required authority businesses exist
const vatBusiness = await client.query(`SELECT * FROM accounter_schema.businesses WHERE id = $1`, [context.vat_business_id]);
expect(vatBusiness.rows).toHaveLength(1);
const taxBusiness = await client.query(`SELECT * FROM accounter_schema.businesses WHERE id = $1`, [context.tax_business_id]);
expect(taxBusiness.rows).toHaveLength(1);
const socialSecurityBusiness = await client.query(`SELECT * FROM accounter_schema.businesses WHERE id = $1`, [context.social_security_business_id]);
expect(socialSecurityBusiness.rows).toHaveLength(1);
// Verify required tax categories exist
const requiredTaxCategoryFields = [
'default_tax_category_id',
'input_vat_tax_category_id',
'output_vat_tax_category_id',
'property_output_vat_tax_category_id',
'tax_expenses_tax_category_id',
'exchange_rate_tax_category_id',
'income_exchange_rate_tax_category_id',
'exchange_rate_revaluation_tax_category_id',
'fee_tax_category_id',
'general_fee_tax_category_id',
'fine_tax_category_id',
'untaxable_gifts_tax_category_id',
'balance_cancellation_tax_category_id',
'development_foreign_tax_category_id',
'development_local_tax_category_id',
'expenses_to_pay_tax_category_id',
'expenses_in_advance_tax_category_id',
'income_to_collect_tax_category_id',
'income_in_advance_tax_category_id',
'salary_excess_expenses_tax_category_id',
];
for (const field of requiredTaxCategoryFields) {
const taxCategoryId = context[field];
expect(taxCategoryId).toBeTruthy();
const taxCategory = await client.query(`SELECT * FROM accounter_schema.tax_categories WHERE id = $1`, [taxCategoryId]);
expect(taxCategory.rows).toHaveLength(1);
}
});
});
it('should be idempotent (safe to call multiple times)', async () => {
await db.withTransaction(async (client) => {
// Call seed twice in same transaction
await seedAdminCore(client);
// Count entities before second call
const countBefore = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.financial_entities`);
const entitiesBeforeSecondCall = parseInt(countBefore.rows[0].count);
// Second call should reuse existing entities
await seedAdminCore(client);
// Count entities after second call - should be same
const countAfter = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.financial_entities`);
const entitiesAfterSecondCall = parseInt(countAfter.rows[0].count);
// Idempotent: no new entities created on second call
expect(entitiesAfterSecondCall).toBe(entitiesBeforeSecondCall);
// Verify only one user_context exists
const userContextCount = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.user_context`);
expect(userContextCount.rows[0].count).toBe('1');
});
});
it('should not leak data between tests (transactional isolation)', async () => {
await db.withTransaction(async (client) => {
const TEMP_NAME = 'seed-admin-context.integration.test.ts: temp rollback entity';
// Insert a throwaway entity inside a transaction
const insertEntity = await client.query(`INSERT INTO accounter_schema.financial_entities (name, type)
VALUES ($1, 'business')
RETURNING id`, [TEMP_NAME]);
const tempId = insertEntity.rows[0].id;
await client.query(`INSERT INTO accounter_schema.businesses (id, owner_id) VALUES ($1, $2)`, [tempId, tempId]);
// Verify exists within the same transaction
const inTx = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.financial_entities WHERE name = $1`, [TEMP_NAME]);
expect(inTx.rows[0].count).toBe('1');
// Note: withTransaction automatically rolls back at the end,
// so the next test will start with a clean slate
});
});
it('should create all expected entity counts', async () => {
await db.withTransaction(async (client) => {
await seedAdminCore(client);
// Count businesses (Admin + 3 authorities = 4)
const businessCount = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.businesses`);
expect(parseInt(businessCount.rows[0].count)).toBeGreaterThanOrEqual(4);
// Count tax categories (3 authority + 12 general + 4 cross-year = 19)
const taxCategoryCount = await client.query(`SELECT COUNT(*) as count FROM accounter_schema.tax_categories`);
expect(parseInt(taxCategoryCount.rows[0].count)).toBeGreaterThanOrEqual(19);
});
});
it('should validate foreign key relationships', async () => {
await db.withTransaction(async (client) => {
const { adminEntityId } = await seedAdminCore(client);
// Get user_context
const userContext = await client.query(`SELECT * FROM accounter_schema.user_context WHERE owner_id = $1`, [adminEntityId]);
const context = userContext.rows[0];
// Verify all business FKs point to valid financial_entities
const businessFields = ['vat_business_id', 'tax_business_id', 'social_security_business_id'];
for (const field of businessFields) {
const businessId = context[field];
const entity = await client.query(`SELECT * FROM accounter_schema.financial_entities WHERE id = $1`, [businessId]);
expect(entity.rows).toHaveLength(1);
expect(entity.rows[0].type).toBe('business');
}
// Verify all tax_category FKs point to valid financial_entities
const taxCategoryFields = [
'default_tax_category_id',
'input_vat_tax_category_id',
'output_vat_tax_category_id',
'property_output_vat_tax_category_id',
];
for (const field of taxCategoryFields) {
const taxCategoryId = context[field];
const entity = await client.query(`SELECT * FROM accounter_schema.financial_entities WHERE id = $1`, [taxCategoryId]);
expect(entity.rows).toHaveLength(1);
expect(entity.rows[0].type).toBe('tax_category');
}
});
});
});
//# sourceMappingURL=seed-admin-context.integration.test.js.map