Skip to content

test(linq): fixture every documented KYC failure code, and stop guess… - #259

Merged
Miracle656 merged 2 commits into
Miracle656:mainfrom
JemimahEkong:test/w087-kyc-error-fixtures
Oct 20, 2026
Merged

Miracle656 merged 2 commits into
Miracle656:mainfrom
JemimahEkong:test/w087-kyc-error-fixtures

Conversation

@JemimahEkong

Copy link
Copy Markdown
Contributor

W087 · Fixtures for the provider's documented KYC failure codes

Closes #244

What changed

  • fixtures/linq/kyc-errors.json, at the repo root: one case per documented code (invalid_nin_format 400, nin_already_used 409, nin_verification_failed 422, kyc_provider_error 502, kyc_internal_error 500), plus two undocumented-code cases and five no-code cases. Each pairs the provider response with the veil answer from POST /ngn/customers/kyc. Clients test against veil; fixtures/README.md says how to vendor it pinned to a commit.
  • src/__tests__/linq/kycErrorFixtures.test.ts (50 tests) drives every case through stubbed global.fetch → the real submitCustomerKyc parser → the real route. The existing route tests hand-built LinqErrors and never touched the parser. No path reaches the provider.
  • Fix: a reasonless 5xx no longer says "Linq rejected the request." That told the user their request was at fault, which is a cause nobody sent. It now reads Linq returned an error without giving a reason (HTTP 500), with the code appended if there was one. 4xx wording is unchanged.
  • Fix: the route's own 11-digit check now returns code: "invalid_nin_format". That check catches almost every malformed NIN before the provider sees it, and it answered with no code. The clients' branch for this code was therefore unreachable for the commonest case. It is the same condition with the same code, so this is not a mapping.
  • docs/ngn-rails.md gains a failure-code table.

Acceptance criteria

  • Every documented code has a fixture and a test. The documented list is restated in the test, so editing the fixture alone cannot redefine it.
  • An unknown code is passed through, not mapped. This is asserted for nin_name_mismatch (with prose) and kyc_queue_unavailable (without).
  • A body with no readable message implies no cause. This is checked for {}, unrelated fields, blank message and code, an empty body, and an HTML gateway page. In each case code is absent, no cause words appear, and a 5xx never says "rejected".
  • The fixtures are shared. One JSON file outside src/ is read by the backend tests and published for the clients.

Things to know

  • The message prose in the documented fixtures is representative, not captured. I couldn't find the provider's verbatim bodies in the repo, its history or online. The codes and statuses come from the 2026-10-14 documentation. The { message, code } envelope is what the parser already reads. customerRef on nin_already_used matches what client.ts already documents. Please replace the prose with a real capture when one exists.
  • The status is remapped and the code is not. kyc_internal_error arrives from the provider as 500 and reaches clients as 502. That is existing sendLinqError behaviour, and the fixture and docs now state it explicitly. Clients should branch on code.
  • Two existing assertions in onramp.test.ts changed. Both pinned the old 5xx wording, so they had to change, and I added a 4xx counterpart.
  • No NIN in any fixture. A test enforces that the file has no 11-digit run and no nin field. Tests use 00000000000.
  • I mutation-checked the suite by reverting each fix in turn. Each revert turned tests red: 14, 8 and 4 failures respectively.

Tests

npm test before: 710/711 passing. After: 760/761 passing. The one failure both times is the pre-existing openapiCoverage.test.ts (W082). npm run typecheck is clean.

…ing a cause (W087)

The provider documented its KYC failure codes on 2026-10-14 and both clients
branch on them, but nothing tested a single one against the shape it arrives
in. The route tests hand-built LinqErrors and skipped the parser.

- fixtures/linq/kyc-errors.json: one case per documented code, plus
  undocumented-code and no-code cases. Each pairs the provider's response with
  wraith's answer, so the clients test against the same contract.
- kycErrorFixtures.test.ts drives every case through stubbed fetch, the real
  parser and the real route. No provider path is reachable.
- A reasonless 5xx no longer reads "Linq rejected the request", which blamed
  the request for a cause nobody sent. 4xx wording is unchanged.
- The route's own 11-digit check now carries code: invalid_nin_format, so the
  clients' branch for it is reachable for the commonest case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvL6Yz2Zfg7spM1F9DXXtz
@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@JemimahEkong Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Miracle656

Copy link
Copy Markdown
Owner

Rebase needed — #261 landed, consolidating the two Linq HTTP clients into a single src/linq/http.ts. Anything touching src/linq/client.ts or the old call() in src/linq/onramp.ts will conflict.

What changed, so the rebase is predictable:

  • One client. src/linq/http.ts is now the only module that names LINQ_BASE_URL, the provider host or the X-API-Key header — and there are guard tests asserting that, so a second client cannot grow back.
  • LinqError kept all its fields, including existingCustomerRef, which I added yesterday for Linq's email_already_in_use response.
  • #250 also landed and fixed main's Typecheck & build, so you should inherit a green one.

Also worth knowing: Integration tests is red on main itself — tests/integration/api.test.ts, the Postgres read path, expected undefined to be 2, failing since at least 10-16. Not yours, and not a reason to hold a rebase.

Push and I will pick it up.

Brings the branch onto Miracle656#261's single Linq client (src/linq/http.ts).

Conflict in src/linq/onramp.ts: this branch changed the reasonless-error
fallback inside onramp's private call(), which Miracle656#261 removed. The change
now lives where every Linq error is built: unexplainedFailure() is in
http.ts and used for both the non-JSON and the JSON fallback; onramp.ts
re-exports it so the branch's public surface is unchanged.

Because there is now one client, the wording applies to every Linq rail,
not only KYC: a reasonless 5xx reads "Linq returned an error without
giving a reason (HTTP 5xx)"; a reasonless 4xx is still "Linq rejected
the request". Two httpClient.test.ts expectations (502 HTML page, 500
oversized body) are updated accordingly — flagging for review, since
they pinned the old wording on all rails.

Verified: typecheck, tsc --noEmit, build; jest 63 suites / 958 tests.
Reverting the fallback to the old wording fails 12 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvL6Yz2Zfg7spM1F9DXXtz
@Miracle656
Miracle656 merged commit 13b2d19 into Miracle656:main Oct 20, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The provider's documented failure codes have no fixtures

2 participants