Test Inventory
Bilko — Test Inventory
Status: ActivePartially stale — Testsrecount Implementedrequired after Kotlin/Ktor migration and Playwright partitioning
Version: 2.01
Last Updated: 2026-03-0205-21
Author: ALAI Documentation Team
This inventory catalogs all implemented tests in Bilko, organized by package and file. It currently contains historical Express/Prisma-era details and must not be used as the source of truth for current test counts. For current policy, use TEST-STRATEGY.md, E2E-TEST-PLAN.md, and DEMO-TESTING-PLAN.md.
Summary
Refresh note (2026-05-21): quick filesystem inventory found apps/api/src/test/kotlin with many Kotlin test files, packages/core/tests, and apps/e2e/tests. The table below is historical until regenerated from the current tree.
| Package |
Test Files |
Test Cases |
Status |
apps/api/tests/ (mock suite) |
11 test files |
~130 |
Implemented |
apps/api/tests/unit/ (unit suite) |
4 test files |
~60 |
Implemented |
apps/api/tests/e2e/ (E2E suite) |
2 test files |
~30 |
Implemented |
apps/api/tests/integration/ (real DB) |
5 test files |
~50 |
Implemented |
packages/core/tests/ (unit) |
5 test files |
121 |
Implemented |
| Total |
27 |
~390 |
Active |
Test Category Breakdown
| Category |
Description |
Requires DB |
Mock suite (tests/*.test.ts) |
API endpoint tests with mocked Prisma — fast, no DB |
No |
Unit suite (tests/unit/) |
Service-layer tests, financial logic, SEF client |
No |
E2E suite (tests/e2e/) |
End-to-end billing flows with mocked services |
No |
Integration suite (tests/integration/) |
Real DB tests (docker-compose.test.yml) |
Yes — PostgreSQL |
Core unit (packages/core/) |
Pure business logic — no HTTP, no DB |
No |
packages/core/tests/ — Unit Tests
Pure unit tests for the @bilko/core financial engine. No database, no HTTP. Uses globals: true (no explicit imports of describe/it/expect).
accounting.test.ts — 20 tests
Tests double-entry bookkeeping engine: validateDoubleEntry, createJournalEntry, calculateTrialBalance.
| Test Name |
What It Tests |
Status |
validateDoubleEntry - returns true for balanced entry |
Debit total equals credit total |
Implemented |
validateDoubleEntry - returns false for unbalanced entry |
Debit ≠ credit |
Implemented |
validateDoubleEntry - returns false for fewer than 2 lines |
Minimum 2 lines required |
Implemented |
validateDoubleEntry - returns false for empty lines |
Empty array → false |
Implemented |
validateDoubleEntry - returns false for negative amounts |
Negative amounts invalid |
Implemented |
validateDoubleEntry - returns false for zero amounts |
Zero amounts invalid |
Implemented |
validateDoubleEntry - handles multiple lines that sum to balanced |
Multi-line balanced entry |
Implemented |
validateDoubleEntry - handles decimal amounts with precision |
NUMERIC(19,4) precision |
Implemented |
createJournalEntry - returns entry when valid and balanced |
Happy path |
Implemented |
createJournalEntry - throws for fewer than 2 lines |
Error: "at least 2 lines" |
Implemented |
createJournalEntry - throws for empty lines array |
Error: "at least 2 lines" |
Implemented |
createJournalEntry - throws for missing description |
Error: "must have a description" |
Implemented |
createJournalEntry - throws for whitespace-only description |
Whitespace = missing |
Implemented |
createJournalEntry - throws for missing date |
Error: "must have a date" |
Implemented |
createJournalEntry - throws for unbalanced entry |
Error shows debit vs credit amounts |
Implemented |
createJournalEntry - throws for negative amount lines |
Negative amounts rejected |
Implemented |
calculateTrialBalance - returns balanced from balanced entries |
isBalanced=true, totals correct |
Implemented |
calculateTrialBalance - groups by account number |
Rows aggregated per account |
Implemented |
calculateTrialBalance - returns empty rows for no transactions |
Empty array → zero totals |
Implemented |
calculateTrialBalance - sorts rows by account number |
Rows sorted ascending |
Implemented |
chart-of-accounts.test.ts — 32 tests
Tests chart of accounts operations: account creation, parent-child hierarchy, account type validation.
| Test Area |
Test Count |
Status |
| Account creation (valid + invalid inputs) |
~10 |
Implemented |
| Account hierarchy (parent-child relationships) |
~8 |
Implemented |
| Account type validation (Asset/Liability/Equity/Revenue/Expense) |
~7 |
Implemented |
| Account code format validation (1xxx-5xxx range) |
~7 |
Implemented |
invoicing.test.ts — 22 tests
Tests invoice number generation, total calculations, and line item validation.
| Test Name |
What It Tests |
Status |
generateInvoiceNumber - generates INV-YYYY-NNN format |
Standard format |
Implemented |
generateInvoiceNumber - increments from last number |
Sequential numbering |
Implemented |
generateInvoiceNumber - pads number to 3 digits |
Zero-padding |
Implemented |
generateInvoiceNumber - handles numbers beyond 999 |
No truncation for 1000+ |
Implemented |
generateInvoiceNumber - throws for empty prefix |
Error: "prefix is required" |
Implemented |
generateInvoiceNumber - throws for whitespace-only prefix |
Whitespace = invalid |
Implemented |
generateInvoiceNumber - throws for negative lastNumber |
Error: "non-negative integer" |
Implemented |
generateInvoiceNumber - throws for non-integer lastNumber |
Float rejected |
Implemented |
calculateInvoiceTotals - calculates line item total |
quantity × unitPrice |
Implemented |
calculateInvoiceTotals - calculates subtotal as sum of lines |
Sum of all line totals |
Implemented |
calculateInvoiceTotals - calculates tax per line item |
lineTotal × taxRate / 100 |
Implemented |
calculateInvoiceTotals - calculates total = subtotal + tax |
Final total |
Implemented |
calculateInvoiceTotals - handles items without taxRate |
Missing tax = 0 |
Implemented |
calculateInvoiceTotals - returns zeros for empty items |
Empty array → all zeros |
Implemented |
calculateInvoiceTotals - handles multiple items with different rates |
Mixed 20%/10% tax |
Implemented |
calculateInvoiceTotals - maintains decimal precision |
3 × 33.33 = 99.99 |
Implemented |
validateLineItem - returns true for valid item |
Happy path |
Implemented |
validateLineItem - returns false for zero quantity |
qty=0 invalid |
Implemented |
validateLineItem - returns false for negative quantity |
qty<0 invalid |
Implemented |
validateLineItem - returns false for negative unitPrice |
price<0 invalid |
Implemented |
validateLineItem - returns false for empty description |
Description required |
Implemented |
validateLineItem - returns false for whitespace-only description |
Whitespace = empty |
Implemented |
multi-currency.test.ts — 24 tests
Tests currency conversion, exchange rate locking, and NUMERIC precision handling.
| Test Area |
Test Count |
Status |
| Currency conversion (EUR/RSD/BAM) |
~8 |
Implemented |
| Exchange rate locking at transaction date |
~6 |
Implemented |
| NUMERIC(19,4) precision (no float drift) |
~5 |
Implemented |
| Edge cases (zero rate, missing rate, same currency) |
~5 |
Implemented |
tax.test.ts — 23 tests
Tests VAT calculations for all supported countries and edge cases.
| Test Area |
Test Count |
Status |
| Serbia PDV (20% standard, 10% reduced, 0% exempt) |
~6 |
Implemented |
| BiH PDV (17% standard) |
~4 |
Implemented |
| Croatia PDV (25% standard, 13% reduced, 5% super-reduced) |
~5 |
Implemented |
| Mixed tax rates on single invoice |
~3 |
Implemented |
| Zero-rate exports |
~2 |
Implemented |
| Reverse VAT / gross-to-net |
~3 |
Implemented |
apps/api/tests/ — Mock API Tests
Integration tests for Express API endpoints. Tests use mocked Prisma client — no real database required. Setup in tests/setup.ts.
setup.ts — Test Infrastructure (not a test file)
Provides:
- Prisma client mock via
vi.mock('../src/lib/prisma')
- Environment variable setup (JWT secrets, rate limits)
createTestUser() — factory for test user objects
generateTestAccessToken() — valid JWT for authenticated requests
generateTestRefreshToken() — valid refresh token
- Constants:
TEST_ORG_ID, TEST_USER_ID, TEST_USER_EMAIL
auth.test.ts — 11 tests
| Test Name |
Route |
Status |
returns 201 with user, organization, and tokens |
POST /auth/register |
Implemented |
returns 400 for duplicate email |
POST /auth/register |
Implemented |
returns 200 with tokens for valid credentials |
POST /auth/login |
Implemented |
returns 401 for invalid password |
POST /auth/login |
Implemented |
returns 401 for non-existent email |
POST /auth/login |
Implemented |
returns 200 with new access token for valid refresh token |
POST /auth/refresh |
Implemented |
returns 401 when no refresh token cookie is present |
POST /auth/refresh |
Implemented |
returns 204 and clears cookie |
POST /auth/logout |
Implemented |
returns 200 with current user when authenticated |
GET /auth/me |
Implemented |
returns 401 when no token provided |
GET /auth/me |
Implemented |
returns 401 for invalid token |
GET /auth/me |
Implemented |
invoices.test.ts — 11 tests
| Test Name |
Route |
Status |
returns 200 with paginated list |
GET /invoices |
Implemented |
returns 401 without auth |
GET /invoices |
Implemented |
returns 201 with created invoice |
POST /invoices |
Implemented |
returns 200 with full invoice |
GET /invoices/:id |
Implemented |
returns 404 when invoice not found |
GET /invoices/:id |
Implemented |
returns 200 when updating draft invoice |
PUT /invoices/:id |
Implemented |
returns 400 when updating sent invoice |
PUT /invoices/:id |
Implemented |
returns 200 when sending invoice (draft -> sent) |
PATCH /invoices/:id/status |
Implemented |
returns 200 when marking invoice as paid (sent -> paid) |
PATCH /invoices/:id/status |
Implemented |
returns 204 when deleting draft invoice |
DELETE /invoices/:id |
Implemented |
returns 400 when deleting sent invoice |
DELETE /invoices/:id |
Implemented |
expenses.test.ts — 9 tests
| Test Area |
Route |
Status |
| List expenses (200 + auth) |
GET /expenses |
Implemented |
| Create expense (201) |
POST /expenses |
Implemented |
| Get expense by ID (200 + 404) |
GET /expenses/:id |
Implemented |
| Update expense (200 + 400 for non-pending) |
PUT /expenses/:id |
Implemented |
| Approve expense (200 + role check) |
PATCH /expenses/:id/approve |
Implemented |
| Delete expense (204 + 400 for approved) |
DELETE /expenses/:id |
Implemented |
| Test Area |
Route |
Status |
| List contacts (200 + auth) |
GET /contacts |
Implemented |
| Create contact (201 + validation) |
POST /contacts |
Implemented |
| Get contact (200 + 404) |
GET /contacts/:id |
Implemented |
| Update contact (200) |
PUT /contacts/:id |
Implemented |
| Delete contact (204) |
DELETE /contacts/:id |
Implemented |
accounts.test.ts — 4 tests
| Test Area |
Route |
Status |
| List chart of accounts (200) |
GET /accounts |
Implemented |
| Get account by ID (200 + 404) |
GET /accounts/:id |
Implemented |
| Create account (201) |
POST /accounts |
Implemented |
| Delete account (204) |
DELETE /accounts/:id |
Implemented |
banking.test.ts — 10 tests
| Test Area |
Route |
Status |
| List bank accounts (200) |
GET /banking/accounts |
Implemented |
| Create bank account (201) |
POST /banking/accounts |
Implemented |
| Import bank statement (200 + validation) |
POST /banking/accounts/:id/import |
Implemented |
| List bank transactions (200) |
GET /banking/accounts/:id/transactions |
Implemented |
| Reconcile transaction (200 + 400 for mismatch) |
POST /banking/accounts/:id/reconcile |
Implemented |
reports.test.ts — 9 tests
| Test Area |
Route |
Status |
| Profit & Loss report (200 + date range) |
GET /reports/profit-loss |
Implemented |
| Balance sheet (200) |
GET /reports/balance-sheet |
Implemented |
| VAT report for RS (200 + correct rates) |
GET /reports/vat |
Implemented |
| Trial balance (200) |
GET /reports/trial-balance |
Implemented |
| Auth required on all report endpoints |
All report routes |
Implemented |
transactions.test.ts — 9 tests
| Test Area |
Route |
Status |
| List transactions (200 + pagination) |
GET /transactions |
Implemented |
| Filter by account (200) |
GET /transactions?accountId= |
Implemented |
| Get transaction by ID (200 + 404) |
GET /transactions/:id |
Implemented |
| Auth required |
All transaction routes |
Implemented |
country.test.ts — 27 tests
Tests the country plugin integration — routes that return country-specific tax configuration.
| Test Area |
Route |
Status |
| Serbian PDV rates (20/10/0) for RS org |
GET /country/tax-rates |
Implemented |
| Bosnian PDV rate (17) for BA org |
GET /country/tax-rates |
Implemented |
| Croatian PDV rates (25/13/5/0) for HR org |
GET /country/tax-rates |
Implemented |
| Invoice number format per country |
GET /country/invoice-format |
Implemented |
| Auth required |
All country routes |
Implemented |
| Unknown country code (400) |
GET /country/tax-rates |
Implemented |
chatbot.test.ts — Chatbot API Tests
| Test Name |
Route |
Status |
returns 200 with assistant response |
POST /chatbot/message |
Implemented |
returns 400 when message is empty |
POST /chatbot/message |
Implemented |
returns 429 when rate limit exceeded |
POST /chatbot/message |
Implemented |
returns 401 without auth |
POST /chatbot/message |
Implemented |
returns 200 with conversation history |
GET /chatbot/history |
Implemented |
returns empty array when no history exists |
GET /chatbot/history |
Implemented |
returns 401 without auth on history |
GET /chatbot/history |
Implemented |
returns 204 when history cleared |
DELETE /chatbot/history |
Implemented |
returns 401 without auth on clear history |
DELETE /chatbot/history |
Implemented |
invoice-gl-reversal.test.ts — Invoice GL Reversal Tests
Tests InvoiceService.cancelInvoice() — when a SENT invoice is cancelled, reversing double-entry GL entries are created to undo the original booking.
| Test Area |
Status |
| Cancels draft invoice (sets status to cancelled, no GL reversal) |
Implemented |
| Cancels sent invoice (creates reversing GL transactions) |
Implemented |
| Reversal debits = original credits (GL stays balanced) |
Implemented |
| Throws 404 when invoice not found |
Implemented |
| Throws 400 when invoice is already cancelled |
Implemented |
| Throws 400 when invoice is paid (cannot cancel paid invoices) |
Implemented |
new-endpoints.test.ts — Additional Endpoint Tests
Tests for supplemental endpoints not covered in the main mock suite.
| Test Area |
Route |
Status |
| Receipt not attached (404) |
GET /expenses/:id/receipt |
Implemented |
| Receipt auth required (401) |
GET /expenses/:id/receipt |
Implemented |
| VAT export PDF (200 / 404) |
GET /reports/vat/export/pdf |
Implemented |
| VAT export XML (200 / 404) |
GET /reports/vat/export/xml |
Implemented |
| Dashboard metrics (200) |
GET /reports/dashboard |
Implemented |
| Dashboard auth required (401) |
GET /reports/dashboard |
Implemented |
| Audit log (200 + owner/admin only) |
GET /security/audit-log |
Implemented |
| Audit log role check (403 for viewer) |
GET /security/audit-log |
Implemented |
| Data export (200 + owner only) |
POST /security/data-export |
Implemented |
| Data export role check (403 for admin) |
POST /security/data-export |
Implemented |
e2e/api.test.ts — Full E2E (no mocks, live server)
End-to-end API integration test. Exercises the full Express application stack with a live server.
e2e/billing-flow.e2e.test.ts — Billing Workflow E2E
Tests the full billing flow through HTTP endpoints with mocked services (no real DB required):
- Create contact
- Create invoice
- Send invoice (draft → sent)
- Mark invoice paid (sent → paid)
- Check P&L shows revenue
- Verify trial balance returns
isBalanced=true
- Multi-currency invoice in EUR with country VAT rates
- Credit note creation
apps/api/tests/unit/ — Unit Tests (service layer)
Service-level unit tests with mocked Prisma. No HTTP layer. Tests business logic in individual service classes.
invoice-service-calculations.test.ts — Invoice Arithmetic
Tests InvoiceService.createInvoice() arithmetic at the service layer. Verifies:
lineTotal = quantity × unitPrice
lineTax = lineTotal × taxRate / 100
subtotal = sum(lineTotals)
taxAmount = sum(lineTaxes)
total = subtotal + taxAmount
baseAmount = total × exchangeRate
| Test Area |
Status |
| Single-line invoice arithmetic |
Implemented |
| Multi-line invoice with mixed tax rates |
Implemented |
| Multi-currency exchange rate application |
Implemented |
| Zero-quantity line items rejected |
Implemented |
| NUMERIC(19,4) precision maintained |
Implemented |
two-factor.test.ts — Two-Factor Authentication Service
Tests TwoFactorService at the service level. Mocks: bcryptjs, speakeasy, qrcode, Prisma.
| Test Name |
Status |
enable - wrong password → throws unauthorized |
Implemented |
enable - correct password → returns secret + QR data URL + 10 codes |
Implemented |
enable - backup codes are hashed before storing |
Implemented |
enable - plaintext backup codes returned once only |
Implemented |
verify - valid TOTP + window=1 → activates 2FA |
Implemented |
verify - invalid TOTP → throws badRequest |
Implemented |
disable - correct password → clears secret + backup codes |
Implemented |
disable - wrong password → throws unauthorized |
Implemented |
sef-submission.test.ts — SEF (Serbia E-Invoicing) Client
Tests SefClient class and InvoiceService.submitToSef() fire-and-forget behavior. HTTP calls are mocked via vi.spyOn(global, 'fetch').
| Test Area |
Status |
SefClient submits UBL XML to SEF sandbox URL |
Implemented |
SefClient uses production URL in production env |
Implemented |
SefClient retries on network failure |
Implemented |
createSefClientFromEnv() reads credentials from env vars |
Implemented |
generateSEFInvoiceXML() produces valid UBL 2.1 XML |
Implemented |
InvoiceService.submitToSef() never re-throws errors |
Implemented |
vat-calculation.test.ts — VAT Calculation Tests (Country Packages)
Tests pure calculation functions from @bilko/country-rs, @bilko/country-ba, and @bilko/country-hr. No Prisma, no HTTP — stateless math functions.
| Country |
Functions Tested |
Status |
| Serbia |
calculateSerbianPDV, calculateNetFromGrossPDV, qualifiesForPausalRegime, requiresVATRegistration, calculateSerbianCIT |
Implemented |
| Bosnia |
calculateBosnianPDV, calculateNetFromGrossPDV, requiresVATRegistration |
Implemented |
| Croatia |
calculateCroatianPDV, calculateNetFromGrossPDV, requiresVATRegistration |
Implemented |
| All rates |
Standard, reduced, zero, super-reduced rates per country |
Implemented |
apps/api/tests/integration/ — Real Database Tests
Integration tests that run against a real PostgreSQL database via docker-compose.test.yml. Requires Docker.
Run with:
cd apps/api
docker-compose -f ../../docker-compose.test.yml up -d
npm run test:integration
auth.integration.test.ts — Auth Integration
Full registration + login + refresh flow against real DB.
| Test Area |
Status |
| Register new organization + owner user |
Implemented |
| Login with correct credentials |
Implemented |
| Refresh access token via cookie |
Implemented |
| Reject duplicate email registration |
Implemented |
invoice.integration.test.ts — Invoice CRUD Integration
Invoice lifecycle against real DB: create → read → update → status change.
| Test Area |
Status |
| Create invoice with line items |
Implemented |
| Read invoice by ID |
Implemented |
| Update draft invoice |
Implemented |
| Send invoice (draft → sent) |
Implemented |
| Mark paid (sent → paid) |
Implemented |
credit-note-gl.integration.test.ts — Credit Note GL Integration
Tests credit note creation against real DB — verifies reversing GL entries balance.
| Test Area |
Status |
| Credit note creates reversing journal entries |
Implemented |
| Reversing entries balance (debits = credits) |
Implemented |
| Original invoice marked as credited |
Implemented |
report.integration.test.ts — Reports Integration
Report generation against real DB with seeded transactions.
| Test Area |
Status |
| P&L report returns revenue/expense data |
Implemented |
| VAT report returns correct tax amounts |
Implemented |
Trial balance returns isBalanced=true |
Implemented |
tenant-isolation.integration.test.ts — Multi-Tenant Security
Verifies multi-tenant isolation: Organization A cannot read or modify Organization B's data.
| Test Area |
Status |
| Org A cannot list Org B's invoices |
Implemented |
| Org A cannot read Org B's invoice by ID |
Implemented |
| Org A cannot update Org B's invoice |
Implemented |
| Org A cannot delete Org B's contact |
Implemented |
| Cross-tenant requests return 404 (not 403 — no data leakage) |
Implemented |
Coverage Tracking
| Module |
Current Status |
Target |
@bilko/core accounting engine |
Tests implemented (20 cases) |
>95% |
@bilko/core invoicing |
Tests implemented (22 cases) |
>95% |
@bilko/core tax/VAT |
Tests implemented (23 cases) |
>95% |
@bilko/core multi-currency |
Tests implemented (24 cases) |
>90% |
@bilko/core chart of accounts |
Tests implemented (32 cases) |
>90% |
| Auth API |
Tests implemented (11 cases) |
>85% |
| Invoices API |
Tests implemented (11 cases) |
>80% |
| Expenses API |
Tests implemented (9 cases) |
>80% |
| Banking API |
Tests implemented (10 cases) |
>75% |
| Reports API |
Tests implemented (9 cases) |
>75% |
| Country/Tax-Rates API |
Tests implemented (27 cases) |
>80% |
| Chatbot API |
Tests implemented (9 cases) |
>80% |
| Security/Audit API |
Tests implemented (via new-endpoints) |
>80% |
| Invoice GL Reversal (service layer) |
Tests implemented |
>90% |
| Two-Factor Auth (service layer) |
Tests implemented (8 cases) |
>90% |
| SEF Client + Submission |
Tests implemented |
>85% |
| VAT Calculations (country packages) |
Tests implemented |
>95% |
| Multi-tenant isolation (real DB) |
Tests implemented (5 scenarios) |
100% |
| Invoice CRUD (real DB) |
Tests implemented |
>80% |
| Credit note GL (real DB) |
Tests implemented |
>90% |
Test Execution Commands
# All tests (from project root — mock + unit + E2E suites)
npm run test
# Core unit tests only
cd packages/core && npx vitest run
# API mock suite only (no DB required)
cd apps/api-express && npx vitest run
# API unit suite only
cd apps/api-express && npx vitest run tests/unit/
# API E2E suite only (mocked services, no DB)
cd apps/api-express && npx vitest run tests/e2e/
# Real DB integration tests (requires docker-compose.test.yml)
docker-compose -f docker-compose.test.yml up -d
cd apps/api-express && npm run test:integration
# Watch mode (re-run on change)
cd packages/core && npx vitest
cd apps/api-express && npx vitest
# Specific file
cd apps/api-express && npx vitest run tests/auth.test.ts
cd apps/api-express && npx vitest run tests/unit/two-factor.test.ts
# With coverage
cd packages/core && npx vitest run --coverage
# Verbose output
npx vitest run --reporter=verbose
Last Updated: 2026-03-02
Status: Active
Total Tests: ~390 across 27 test files (mock + unit + E2E + integration)