UNPKG

@accounter/server

Version:
246 lines (202 loc) • 7.81 kB
import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { ensureFinancialEntity } from './seed-helpers.js'; import { qualifyTable } from './test-db-config.js'; import { EntityValidationError } from './seed-errors.js'; import { TestDatabase } from './db-setup.js'; describe('ensureFinancialEntity', () => { let db: TestDatabase; beforeAll(async () => { db = new TestDatabase(); await db.connect(); }); afterAll(async () => { await db.close(); }); it('should create a new financial entity when it does not exist', async () => db.withTransaction(async client => { const result = await ensureFinancialEntity(client, { name: 'Test Entity', type: 'business', }); expect(result).toHaveProperty('id'); expect(typeof result.id).toBe('string'); // Verify it was inserted const checkResult = await client.query( `SELECT id, name, type, owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`, [result.id], ); expect(checkResult.rows.length).toBe(1); expect(checkResult.rows[0].name).toBe('Test Entity'); expect(checkResult.rows[0].type).toBe('business'); expect(checkResult.rows[0].owner_id).toBeNull(); })); it('should return existing entity when called twice with same parameters', async () => db.withTransaction(async client => { const firstResult = await ensureFinancialEntity(client, { name: 'Duplicate Test', type: 'tax_category', }); const secondResult = await ensureFinancialEntity(client, { name: 'Duplicate Test', type: 'tax_category', }); expect(firstResult.id).toBe(secondResult.id); // Verify only one row exists const countResult = await client.query( `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1 AND type = $2`, ['Duplicate Test', 'tax_category'], ); expect(parseInt(countResult.rows[0].count)).toBe(1); })); it('should handle entities with owner_id', async () => db.withTransaction(async client => { // First create an owner entity (business) const ownerEntityResult = await ensureFinancialEntity(client, { name: 'Owner Entity', type: 'business', }); // Create corresponding business record (required for foreign key) await client.query(`INSERT INTO ${qualifyTable('businesses')} (id, owner_id) VALUES ($1, $2)`, [ ownerEntityResult.id, ownerEntityResult.id, ]); // Create owned entity (tax_category) const result = await ensureFinancialEntity(client, { name: 'Owned Entity', type: 'tax_category', ownerId: ownerEntityResult.id, }); expect(result).toHaveProperty('id'); // Verify owner_id is set correctly const checkResult = await client.query( `SELECT owner_id FROM ${qualifyTable('financial_entities')} WHERE id = $1`, [result.id], ); expect(checkResult.rows[0].owner_id).toBe(ownerEntityResult.id); })); it('should be idempotent for entities with owner_id', async () => db.withTransaction(async client => { const ownerEntityResult = await ensureFinancialEntity(client, { name: 'Owner Business', type: 'business', }); // Create corresponding business record await client.query(`INSERT INTO ${qualifyTable('businesses')} (id, owner_id) VALUES ($1, $2)`, [ ownerEntityResult.id, ownerEntityResult.id, ]); const firstResult = await ensureFinancialEntity(client, { name: 'Child Entity', type: 'tax_category', ownerId: ownerEntityResult.id, }); const secondResult = await ensureFinancialEntity(client, { name: 'Child Entity', type: 'tax_category', ownerId: ownerEntityResult.id, }); expect(firstResult.id).toBe(secondResult.id); // Verify only one row const countResult = await client.query( `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1 AND type = $2 AND owner_id = $3`, ['Child Entity', 'tax_category', ownerEntityResult.id], ); expect(parseInt(countResult.rows[0].count)).toBe(1); })); it('should distinguish entities by type', async () => db.withTransaction(async client => { const businessResult = await ensureFinancialEntity(client, { name: 'Same Name', type: 'business', }); const taxCategoryResult = await ensureFinancialEntity(client, { name: 'Same Name', type: 'tax_category', }); expect(businessResult.id).not.toBe(taxCategoryResult.id); })); it('should distinguish entities by owner_id', async () => db.withTransaction(async client => { const owner1Entity = await ensureFinancialEntity(client, { name: 'Owner 1', type: 'business', }); await client.query(`INSERT INTO ${qualifyTable('businesses')} (id, owner_id) VALUES ($1, $2)`, [ owner1Entity.id, owner1Entity.id, ]); const owner2Entity = await ensureFinancialEntity(client, { name: 'Owner 2', type: 'business', }); await client.query(`INSERT INTO ${qualifyTable('businesses')} (id, owner_id) VALUES ($1, $2)`, [ owner2Entity.id, owner2Entity.id, ]); const entity1 = await ensureFinancialEntity(client, { name: 'Same Child', type: 'tax_category', ownerId: owner1Entity.id, }); const entity2 = await ensureFinancialEntity(client, { name: 'Same Child', type: 'tax_category', ownerId: owner2Entity.id, }); expect(entity1.id).not.toBe(entity2.id); })); it('should handle null vs undefined owner_id consistently', async () => db.withTransaction(async client => { const result1 = await ensureFinancialEntity(client, { name: 'No Owner Entity', type: 'business', ownerId: undefined, }); const result2 = await ensureFinancialEntity(client, { name: 'No Owner Entity', type: 'business', }); expect(result1.id).toBe(result2.id); })); it('should not leak data between tests', async () => db.withTransaction(async client => { // This test verifies that ROLLBACK works correctly // Create an entity in this test await ensureFinancialEntity(client, { name: 'Transient Entity', type: 'business', }); // The entity should exist within this transaction const checkInTransaction = await client.query( `SELECT COUNT(*) FROM ${qualifyTable('financial_entities')} WHERE name = $1`, ['Transient Entity'], ); expect(parseInt(checkInTransaction.rows[0].count)).toBe(1); // After ROLLBACK in afterEach, this data won't be visible in next test })); // Validation tests it('should reject empty entity name', async () => db.withTransaction(async client => { await expect( ensureFinancialEntity(client, { name: '', type: 'business', }), ).rejects.toThrow(EntityValidationError); })); it('should reject invalid entity type', async () => db.withTransaction(async client => { await expect( ensureFinancialEntity(client, { name: 'Test Entity', type: 'invalid_type' as any, }), ).rejects.toThrow(EntityValidationError); })); it('should reject whitespace-only name', async () => db.withTransaction(async client => { await expect( ensureFinancialEntity(client, { name: ' ', type: 'business', }), ).rejects.toThrow(EntityValidationError); })); });