Files
accounted/lib/bookkeeping/__tests__/account-backfill.test.ts
T
Jakob WennbergandClaude Sonnet 5 ec27228a8e style: remove em/en dashes repo-wide, add CLAUDE.md rule against them (#890)
Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.

Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.

Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.

Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-04 15:58:06 +02:00

133 lines
4.2 KiB
TypeScript

import { describe, it, expect, vi } from 'vitest'
import { backfillStandardBASAccounts } from '../account-backfill'
/**
* Flexible supabase mock: every chain method returns the chain; awaiting it
* resolves the queued result for that table+operation. Inserts are captured.
*/
function createMockSupabase(opts: {
existingRows?: { account_number: string }[]
insertError?: { code?: string; message: string } | null
}) {
const inserts: unknown[] = []
const makeChain = (result: { data?: unknown; error?: unknown }) => {
const chain: Record<string, unknown> = {}
const handler: ProxyHandler<object> = {
get(_t, prop) {
if (prop === 'then') {
return (resolve: (v: unknown) => void) =>
resolve({ data: result.data ?? null, error: result.error ?? null })
}
return (..._args: unknown[]) => new Proxy(chain, handler)
},
}
return new Proxy(chain, handler)
}
const supabase = {
from: vi.fn().mockImplementation(() => {
const base: Record<string, unknown> = {
select: () => makeChain({ data: opts.existingRows ?? [] }),
insert: (rows: unknown) => {
inserts.push(rows)
return makeChain({ error: opts.insertError ?? null })
},
}
return base
}),
}
return { supabase, inserts }
}
describe('backfillStandardBASAccounts', () => {
it('seeds a standard BAS account with full reference metadata', async () => {
const { supabase, inserts } = createMockSupabase({ existingRows: [] })
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['3740'],
)
expect(result).toEqual(['3740'])
expect(inserts).toHaveLength(1)
const rows = inserts[0] as Record<string, unknown>[]
expect(rows).toHaveLength(1)
expect(rows[0]).toMatchObject({
company_id: 'company-1',
user_id: 'user-1',
account_number: '3740',
account_name: 'Öres- och kronutjämning',
account_class: 3,
account_group: '37',
is_active: true,
is_system_account: false,
plan_type: 'full_bas',
})
})
it('skips numbers that are not standard BAS accounts', async () => {
const { supabase, inserts } = createMockSupabase({ existingRows: [] })
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['9999'],
)
expect(result).toEqual([])
expect(inserts).toHaveLength(0)
})
it('never resurrects an existing (deactivated) account', async () => {
// The caller saw 3740 as missing because it is INACTIVE: deactivation is
// a deliberate user choice, so the backfill must not touch the row.
const { supabase, inserts } = createMockSupabase({
existingRows: [{ account_number: '3740' }],
})
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['3740'],
)
expect(result).toEqual([])
expect(inserts).toHaveLength(0)
})
it('treats a concurrent duplicate insert (23505) as success', async () => {
const { supabase } = createMockSupabase({
existingRows: [],
insertError: { code: '23505', message: 'duplicate key value' },
})
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['3740'],
)
expect(result).toEqual(['3740'])
})
it('returns [] on a non-duplicate insert error', async () => {
const { supabase } = createMockSupabase({
existingRows: [],
insertError: { code: '42501', message: 'permission denied' },
})
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['3740'],
)
expect(result).toEqual([])
})
it('seeds only the missing standard accounts from a mixed list', async () => {
const { supabase, inserts } = createMockSupabase({
existingRows: [{ account_number: '6580' }],
})
const result = await backfillStandardBASAccounts(
supabase as never, 'company-1', 'user-1', ['3740', '6580', 'XYZ1'],
)
expect(result).toEqual(['3740'])
const rows = inserts[0] as Record<string, unknown>[]
expect(rows.map((r) => r.account_number)).toEqual(['3740'])
})
})