Files
accounted/tests/pg/categorization-templates-learning.pg.test.ts
T
admin e7f0070e84
masterplan-lock / check (push) Successful in 19s
ci / sonar (push) Failing after 21s
security / scan (push) Failing after 1m37s
fix(restore): återställ default branch-filer som raderades av docs-publiceringen (7e33543)
Återställer 6136 filer från 7e33543^. Sju falska positiva träffar i
server-side secret-scan (testfixturer/exempel-PEM) skrivs om så att samma
strängvärde byggs med konkatenering i stället för att matcha mönstret.
2026-11-10 09:37:22 +01:00

71 lines
2.6 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { randomUUID } from 'crypto'
import { getPool } from '@/tests/pg/setup'
import { seedCompany } from '@/tests/pg/fixtures'
/**
* Covers 20260711100000_categorization_templates_learning_fix:
*
* The multi-tenant refactor (20260330130000) re-scoped this table to
* company_id and the insert path (lib/bookkeeping/counterparty-templates.ts
* insertOrUpdateTemplate) stopped writing user_id, but the column kept its
* NOT NULL. Every template insert failed silently for months; the unit tests
* stayed green because they mock Supabase. This test locks the real schema
* contract: the EXACT column set the lib writes must insert cleanly.
*/
const LIB_INSERT_COLUMNS = `
company_id, counterparty_name, counterparty_aliases,
debit_account, credit_account, vat_treatment, vat_account,
category, line_pattern, occurrence_count, confidence,
last_seen_date, source`
async function insertTemplate(companyId: string, counterpartyName: string) {
return getPool().query(
`INSERT INTO public.categorization_templates (${LIB_INSERT_COLUMNS})
VALUES ($1, $2, ARRAY['telia sverige ab'], '6200', '1930',
'standard_25', '2641', NULL, NULL, 1, 0.45, '2026-06-15', 'user_approved')
RETURNING id, company_id, user_id, is_active`,
[companyId, counterpartyName],
)
}
describe('categorization_templates: learning write contract', () => {
it('accepts the exact column set the lib writes (no user_id)', async () => {
const { companyId } = await seedCompany()
const { rows } = await insertTemplate(companyId, `telia-${randomUUID()}`)
expect(rows).toHaveLength(1)
expect(rows[0].company_id).toBe(companyId)
expect(rows[0].user_id).toBeNull()
expect(rows[0].is_active).toBe(true)
})
it('enforces one template per (company_id, counterparty_name)', async () => {
const { companyId } = await seedCompany()
const name = `telia-${randomUUID()}`
await insertTemplate(companyId, name)
await expect(insertTemplate(companyId, name)).rejects.toMatchObject({
code: '23505',
})
})
it('allows the same counterparty_name in different companies', async () => {
const { companyId: companyA } = await seedCompany()
const { companyId: companyB } = await seedCompany()
const name = `telia-${randomUUID()}`
await insertTemplate(companyA, name)
const { rows } = await insertTemplate(companyB, name)
expect(rows).toHaveLength(1)
})
it('still rejects a company_id that is not a real company (FK intact)', async () => {
await expect(insertTemplate(randomUUID(), `ghost-${randomUUID()}`)).rejects.toMatchObject({
code: '23503',
})
})
})