Test Case Template
Test Case Template
Project: Bilko Version: 1.0 Date: 2026-02-25 Author: Ops Architect Status: Final (Template) Reviewers: Tech Lead, Alem Bašić
Document History
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | 2026-02-23 | Ops Architect | Initial draft |
| 1.0 | 2026-02-25 | ALAI Documentation Team | Finalized as reusable template |
How to Use This Template
Copy the relevant section below for each test case. Financial and accounting test cases require explicit precision validation (NUMERIC(19,4)) and country-specific VAT rates.
Unit Test Case Template
// File: apps/api/src/utils/__tests__/<module>.test.ts
import { describe, it, expect } from 'vitest';
import { <functionName> } from '../<module>';
/**
* TC-ID: UNIT-XXX
* Module: <module name>
* Priority: P0 / P1 / P2
* Author: <developer name>
* Date: YYYY-MM-DD
*/
describe('<functionName>', () => {
// STANDARD CASE
it('<description of what is tested>', () => {
// Arrange
const input = <input value>;
// Act
const result = <functionName>(input);
// Assert
expect(result).toBe(<expected value>);
});
// EDGE CASE
it('handles <edge case description>', () => {
// Arrange
const input = <edge case input>;
// Act + Assert
expect(() => <functionName>(input)).toThrow('<error message>');
// OR
expect(<functionName>(input)).toBeNull();
});
});
Integration Test Case Template
// File: apps/api/src/routes/__tests__/<route>.test.ts
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import { app } from '../../app';
import { prisma } from '../../lib/prisma';
import { createTestOrg, createTestUser, loginTestUser } from '../helpers';
/**
* TC-ID: INT-XXX
* Endpoint: <METHOD> <path>
* Priority: P0 / P1 / P2
* Author: <developer name>
* Date: YYYY-MM-DD
*/
describe('<METHOD> <path>', () => {
let authToken: string;
let organizationId: string;
beforeEach(async () => {
// Fresh organization + user per test
const org = await createTestOrg();
organizationId = org.id;
authToken = await loginTestUser(org.id);
});
it('<success scenario description>', async () => {
// Arrange
const payload = { /* request body */ };
// Act
const res = await request(app)
.post('/api/v1/<path>')
.set('Authorization', `Bearer ${authToken}`)
.send(payload);
// Assert
expect(res.status).toBe(201);
expect(res.body.<field>).toBe(<expected>);
});
it('returns 401 without auth', async () => {
const res = await request(app).post('/api/v1/<path>').send({});
expect(res.status).toBe(401);
});
it('returns 403 for cross-org access', async () => {
// Create resource in a different org
const otherOrg = await createTestOrg();
const otherResource = await prisma.<model>.create({
data: { organizationId: otherOrg.id, /* ... */ }
});
const res = await request(app)
.get(`/api/v1/<path>/${otherResource.id}`)
.set('Authorization', `Bearer ${authToken}`);
expect(res.status).toBe(404); // 404, not 403 (no enumeration)
});
});
Accounting-Specific Test Cases (Bilko)
Double-Entry Balance Verification
// TC-ID: ACCT-001
// Priority: P0
// Description: Every financial transaction must have balanced debit and credit entries
it('validates double-entry balance — debits must equal credits', () => {
const entry = {
debitAccountId: 'acc_receivables',
creditAccountId: 'acc_revenue',
amount: new Decimal('50000.0000'), // NUMERIC(19,4)
currencyCode: 'RSD',
};
expect(() => validateDoubleEntry(entry)).not.toThrow();
const debitTotal = new Decimal('1000.0000');
const creditTotal = new Decimal('999.9999'); // Unbalanced
expect(() => validateDoubleEntry({ ...entry, creditAmount: creditTotal }))
.toThrow(
'Double-entry imbalance: debit 1000.0000 ≠ credit 999.9999',
);
});
VAT Calculation Accuracy by Country
// TC-ID: ACCT-002
// Priority: P0
// Description: VAT must be calculated per country-specific rules with NUMERIC precision
describe('VAT calculation accuracy', () => {
it('Serbia RS — standard rate 20% on 1000.0000 RSD', () => {
const result = calculateVAT(new Decimal('1000.0000'), 'RS', 'standard');
expect(result.vatAmount.toString()).toBe('200.0000');
expect(result.total.toString()).toBe('1200.0000');
expect(result.rate).toBe(20);
});
it('Bosnia BiH — standard rate 17% on 100.0000 BAM', () => {
const result = calculateVAT(new Decimal('100.0000'), 'BA', 'standard');
expect(result.vatAmount.toString()).toBe('17.0000');
expect(result.total.toString()).toBe('117.0000');
expect(result.rate).toBe(17);
});
it('Croatia HR — standard rate 25% on 100.0000 EUR', () => {
const result = calculateVAT(new Decimal('100.0000'), 'HR', 'standard');
expect(result.vatAmount.toString()).toBe('25.0000');
expect(result.total.toString()).toBe('125.0000');
expect(result.rate).toBe(25);
});
it('exports zero-rated — all countries', () => {
for (const country of ['RS', 'BA', 'HR']) {
const result = calculateVAT(new Decimal('5000.0000'), country, 'zero');
expect(result.vatAmount.toString()).toBe('0.0000');
expect(result.rate).toBe(0);
}
});
it('NUMERIC precision — no floating point drift', () => {
// 0.1 + 0.2 ≠ 0.3 in IEEE 754 — must use Decimal
const result = calculateVAT(new Decimal('0.1000'), 'RS', 'standard');
expect(result.vatAmount.toString()).toBe('0.0200'); // Not 0.020000000000000004
});
});
Currency Conversion with Rate Locking
// TC-ID: ACCT-003
// Priority: P0
// Description: Exchange rate must be locked at transaction date, not current rate
it('locks exchange rate at transaction date — not current rate', async () => {
const transactionDate = new Date('2026-01-15');
const historicalRate = new Decimal('117.5000'); // EUR/RSD on that date
// Seed historical rate
await prisma.exchangeRate.create({
data: {
fromCurrency: 'EUR',
toCurrency: 'RSD',
rate: historicalRate,
effectiveDate: transactionDate,
},
});
const result = await lockExchangeRate('EUR', 'RSD', transactionDate);
expect(result.toString()).toBe('117.5000');
// Current rate is different — should not matter
await prisma.exchangeRate.create({
data: {
fromCurrency: 'EUR',
toCurrency: 'RSD',
rate: new Decimal('118.2000'),
effectiveDate: new Date(),
},
});
const lockedResult = await lockExchangeRate('EUR', 'RSD', transactionDate);
expect(lockedResult.toString()).toBe('117.5000'); // Same historical rate
});
Invoice Total Calculation with Mixed VAT Rates
// TC-ID: ACCT-004
// Priority: P0
// Description: Multi-item invoices with different VAT rates must calculate correctly
it('invoice with mixed VAT rates — NUMERIC precision throughout', () => {
const items = [
{ description: 'Consulting', quantity: 10, unitPrice: new Decimal('5000.0000'), taxRate: 20 },
{ description: 'Books', quantity: 1, unitPrice: new Decimal('2500.0000'), taxRate: 0 }, // zero-rated
];
const totals = calculateInvoiceTotals(items);
expect(totals.subtotal.toString()).toBe('52500.0000');
expect(totals.taxAmount.toString()).toBe('10000.0000'); // Only first item taxed
expect(totals.totalAmount.toString()).toBe('62500.0000');
expect(totals.items[0].taxAmount.toString()).toBe('10000.0000');
expect(totals.items[1].taxAmount.toString()).toBe('0.0000');
});
E2E Test Case Template
// File: apps/e2e/tests/<flow>.spec.ts
import { test, expect } from '@playwright/test';
/**
* TC-ID: E2E-XXX
* Flow: <flow name>
* Priority: P0 / P1
* Author: <developer name>
* Date: YYYY-MM-DD
*/
test.describe('<Flow Name>', () => {
test.beforeEach(async ({ page }) => {
// Login with test user
await page.goto('/login');
await page.fill('input[name="email"]', '[email protected]');
await page.fill('input[name="password"]', 'demo123');
await page.click('button[type="submit"]');
await expect(page).toHaveURL('/dashboard');
});
test('<scenario description>', async ({ page }) => {
// Navigate
await page.goto('/<route>');
// Interact
await page.click('<selector>');
await page.fill('<selector>', '<value>');
// Assert
await expect(page.locator('<selector>')).toContainText('<expected>');
await expect(page).toHaveURL(/<url-pattern>/);
});
});
Test Case Register
Track all test cases in TEST-INVENTORY.md. Each test case needs:
| Field | Description |
|---|---|
| ID | UNIT-XXX, INT-XXX, ACCT-XXX, E2E-XXX |
| Title | Brief description |
| Priority | P0 / P1 / P2 |
| Type | Unit / Integration / E2E |
| File | Relative path to test file |
| Status | Not implemented / Pass / Fail / Skipped |
| Financial logic | Yes / No (if Yes, requires NUMERIC precision assertion) |
Related Documents
Approval
| Role | Name | Date | Signature |
|---|---|---|---|
| Author | Ops Architect | 2026-02-23 | |
| Reviewer | Tech Lead | ||
| Approver | Alem Bašić |