Files
accounted/lib/providers/fortnox/__tests__/permission-error.test.ts
T
e733ab7c43 fix(arsredovisning): unblock the signing flow, accept foreign parent org nr, explain Fortnox underlag failures (#1738)
Batch from a real migration walkthrough (Fortnox -> Accounted, 2026-08-20):

- Årsredovisning: the "Låst version" select was empty with no explanation
  because the only version was a draft and "Lås version för underskrift"
  is disabled while the four Lagstadgade upplysningar checkboxes and the
  content confirmation count as blockers. The select is now disabled with a
  hint that names the blocker count and links to Fullständighetskontroll,
  the four AR-NOTE-*-UNCONFIRMED issues carry remediation text, the lock
  button explains why it is grey, and "Markera som signerad" says what it
  still needs (locked version, bevisreferens, date).
- Moderföretagets org.nr accepts a foreign registration identifier
  (CHE-123.456.789, HRB 12345, 923 609 016); personnummer shapes stay out.
- Fortnox underlag discovery: log status, body and Fortnox's message on
  failure, show the message in the UI, treat a 400 with behörighet/scope
  text as scopes-required, and fall back to an unfiltered
  voucherfileconnections list when the financialyear filter answers 400.
- Kontomapping: the Momskod column had min-w only; table-fixed collapsed it
  and its selects overflowed into Konfidens. Real w-72 now.
- SIE import warnings pluralise correctly for one skipped voucher; the
  Verifikationsserie option says the source series is preserved.

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-20 13:25:06 +02:00

52 lines
2.0 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { FortnoxApiError, fortnoxErrorMessage, isFortnoxPermissionError } from '../client';
describe('fortnoxErrorMessage', () => {
it('reads ErrorInformation.message from a Fortnox error body', () => {
const error = new FortnoxApiError(
'Fortnox API error: 400 Bad Request',
400,
'{"ErrorInformation":{"error":1,"message":"Kan inte hitta kontot.","code":2000423}}',
);
expect(fortnoxErrorMessage(error)).toBe('Kan inte hitta kontot.');
});
it('falls back to the raw body when it is not JSON and to null when empty', () => {
expect(fortnoxErrorMessage(new FortnoxApiError('x', 500, 'Gateway timeout'))).toBe(
'Gateway timeout',
);
expect(fortnoxErrorMessage(new FortnoxApiError('x', 500, ''))).toBeNull();
expect(fortnoxErrorMessage(new Error('not fortnox'))).toBeNull();
});
});
describe('isFortnoxPermissionError', () => {
it('treats 403 as a permission failure regardless of body', () => {
expect(isFortnoxPermissionError(new FortnoxApiError('forbidden', 403))).toBe(true);
});
it('treats a 400 with a behörighet/scope/licens message as a permission failure', () => {
const error = new FortnoxApiError(
'Fortnox API error: 400 Bad Request',
400,
'{"ErrorInformation":{"error":1,"message":"Du saknar behörighet till denna resurs.","code":2000663}}',
);
expect(isFortnoxPermissionError(error)).toBe(true);
expect(
isFortnoxPermissionError(
new FortnoxApiError('x', 400, '{"ErrorInformation":{"message":"Invalid scope"}}'),
),
).toBe(true);
});
it('does not classify an ordinary 400 or a non-Fortnox error as permission', () => {
expect(
isFortnoxPermissionError(
new FortnoxApiError('x', 400, '{"ErrorInformation":{"message":"Kan inte hitta kontot."}}'),
),
).toBe(false);
expect(isFortnoxPermissionError(new FortnoxApiError('x', 400, ''))).toBe(false);
expect(isFortnoxPermissionError(new Error('boom'))).toBe(false);
});
});