@accounter/server
Version:
Accounter GraphQL server
171 lines • 8 kB
JavaScript
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;
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',
})).rejects.toThrow(EntityValidationError);
}));
it('should reject whitespace-only name', async () => db.withTransaction(async (client) => {
await expect(ensureFinancialEntity(client, {
name: ' ',
type: 'business',
})).rejects.toThrow(EntityValidationError);
}));
});
//# sourceMappingURL=seed-helpers.financial-entity.test.js.map