Files
accounted/lib/bokslut/ixbrl/__tests__/testbank-e2e.manual.test.ts
T
Jakob WennbergandClaude Fable 5 fd1c266cb0 chore: salvage unmerged work from the 2026-07-16 worktree audit (#1043)
* chore: salvage unmerged work from the 2026-07-16 worktree audit

Four items survived the 43-file dirty-tree audit as genuinely unmerged:

- CLAUDE.md: Definition of Done rule 9, the last mile is verified
  in-session, not assumed (project-level counterpart of the switch-on
  check; cloud agents only see the repo file).
- DECISIONS.md: eight decision lines from 2026-07-09 to 2026-07-15,
  condensed and scrubbed of production identifiers for the public repo.
- .claude/skills/loop-ignite: skill that audits the agentic loops and
  ignites dead ones; must live on main for cloud routines to load it.
- lib/bokslut/ixbrl testbank manual E2E: encodes the working testbank
  endpoints and the Luhn-valid test pnr (the documented one fails);
  skipped unless BOLAGSVERKET_TESTBANK_E2E=1, so zero CI cost.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: address review findings on the salvage batch

- testbank e2e: kontrollera returns HTTP 200 even for invalid documents
  (outcome is in utfall), so assert zero typ='error' entries; also assert
  grunduppgifter returns a company name, not just the echoed orgnr.
- loop-ignite: make ignition explicitly idempotent (enable/repair an
  existing trigger before creating, never duplicate).

Skipped the fourth finding (require an observed firing as switch-on
proof): a just-created cron cannot have fired yet; the audit table
already reports last observed run per loop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:35:55 +02:00

69 lines
2.8 KiB
TypeScript

/**
* Manual E2E against Bolagsverket's testbänk (static test data). Skipped
* unless BOLAGSVERKET_TESTBANK_E2E=1: requires the firewall opening for our
* public IP (ordered via api@bolagsverket.se, confirmed 2026-06-18).
*
* Run: BOLAGSVERKET_TESTBANK_E2E=1 npx vitest run lib/bokslut/ixbrl/__tests__/testbank-e2e.manual.test.ts
*/
import { describe, expect, it } from 'vitest'
import { generateK2IxbrlDocument } from '../document/k2-document'
import { makeInput } from './fixtures'
const BASE = 'https://api-accept2.bolagsverket.se/testapi'
// Static test env only accepts these (ANSLUTNINGSANVISNING §3.1); the pnr
// must match (19|20)\d{10} and pass Luhn: GUIDE's documented 190001010106
// fails Luhn, 190001010107 passes and is accepted.
const PNR = '190001010107'
const ORGNR = '1234567890'
const enabled = process.env.BOLAGSVERKET_TESTBANK_E2E === '1'
async function post(path: string, body: unknown) {
const res = await fetch(`${BASE}${path}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
})
const text = await res.text()
return { status: res.status, json: JSON.parse(text) as Record<string, unknown> }
}
describe.skipIf(!enabled)('Bolagsverket testbänk E2E', () => {
it('grunduppgifter returns the static test company', async () => {
const res = await fetch(`${BASE}/hamta-arsredovisningsinformation/v1.4/grunduppgifter/${ORGNR}`)
expect(res.status).toBe(200)
const json = (await res.json()) as { orgnr: string; namn: string }
expect(json.orgnr).toBe(ORGNR)
expect(json.namn).toBeTruthy()
console.log('grunduppgifter:', json.namn)
})
it('skapa-inlamningtoken then kontrollera accepts our generated K2 document', async () => {
const input = makeInput()
input.company.orgNumber = '123456-7890'
input.company.name = 'Aktiebolaget Specifik Konsult'
const { xhtml } = generateK2IxbrlDocument(input)
console.log(`generated iXBRL: ${(xhtml.length / 1024).toFixed(1)} KiB`)
const tokenRes = await post('/lamna-in-arsredovisning/v2.1/skapa-inlamningtoken/', {
pnr: PNR,
orgnr: ORGNR,
})
expect(tokenRes.status).toBe(200)
const token = tokenRes.json.token as string
expect(token).toBeTruthy()
const kontrollRes = await post(`/lamna-in-arsredovisning/v2.1/kontrollera/${token}`, {
handling: { fil: Buffer.from(xhtml, 'utf8').toString('base64'), typ: 'arsredovisning_komplett' },
})
console.log('kontrollera status:', kontrollRes.status)
console.log(JSON.stringify(kontrollRes.json, null, 2))
expect(kontrollRes.status).toBe(200)
// kontrollera answers 200 even for invalid documents; the validation
// outcome lives in utfall, so acceptance means zero error entries.
expect(kontrollRes.json.utfall).toEqual(
expect.not.arrayContaining([expect.objectContaining({ typ: 'error' })]),
)
}, 120_000)
})