Skip to content

test(nor): add fødselsnummer, D-nummer, checksum/parse tests + D-nummer support#96

Merged
AngusHsu merged 7 commits intomainfrom
idnumbers-node-issue-28
Apr 7, 2026
Merged

test(nor): add fødselsnummer, D-nummer, checksum/parse tests + D-nummer support#96
AngusHsu merged 7 commits intomainfrom
idnumbers-node-issue-28

Conversation

@AngusHsu
Copy link
Copy Markdown
Contributor

@AngusHsu AngusHsu commented Apr 7, 2026

Summary

Closes #28, #29, #30 — test coverage for Norway NationalID, milestone v1.6.0.

  • Adds D-nummer support to NationalID.parse(): detects DD field 41–71, subtracts 40 to recover the actual day, and exposes a new required idType: 'fodselsnummer' | 'd-nummer' discriminator on NationalIdParseResult.
  • Adds 49 tests in a single file (src/__tests__/issue-28-30-nor-national-id.test.ts) covering all three issues: fødselsnummer validation across 1800s/1900s/2000s centuries, D-nummer validation (1900s + 2000s centuries, DD=41/55/71 boundaries, DD=40/72 rejection, DD=32 gap rejection), mod-11 checksum, parse() output fields, and public API integration.
  • Cleanup: removed dead try/catch around new Date() (JS Date never throws on overflow), added missing typeof string guard in parse() to mirror validate(), extracted mmNum/fullYear to avoid redundant parseInt calls.

Python parity deviation (documented)

The Python idnumbers source library currently rejects D-nummer inputs because its parse() calls date(yyyy, mm, int(dd)) directly and raises ValueError for dd >= 32. Issue #29 explicitly requires valid D-nummer validation, so this PR extends parse() in TypeScript only. The checksum algorithm and all other behaviors remain identical to Python. The deviation is documented in src/countries/nor/nationalId.ts and CHANGELOG.md. The Python library should add the same logic to restore full parity.

Changes

File Lines
src/countries/nor/nationalId.ts +41 / −27
src/__tests__/issue-28-30-nor-national-id.test.ts +255 (new)
CHANGELOG.md +12

Test plan

  • New file issue-28-30-nor-national-id.test.ts: 49 tests across 5 describe blocks (fødselsnummer, D-nummer, checksum, parse, integration) — all passing
  • Full Jest suite: 1519/1519 passing, no regressions
  • Registry count invariant (parseIdInfo-migration.test.ts): still exactly 80 primary validators
  • Pre-commit hooks (Prettier + tsc + Jest) pass on every commit
  • All test vectors independently checksum-verified with mod-11 calculation
  • DD=40/72 boundary tests use checksum-valid vectors (40019000119/72019000290) so rejection is purely from date overflow, not checksum failure
  • Leap-year reject test (29029900013) asserts parse() returns null directly to isolate the date-validation path

Review rounds

Internal review + 3 external review rounds (reviewer agent). All P0/P1 items resolved:

  • R1 (internal): fixed misleading 29029600013 test label (individual 000, female), added 1800s century vector (10107055008)
  • R2 external round 1: fixed DD=40 test name/vector; removed duplicate leap-year test
  • R3 external round 2: made idType optional → then round 3 made it required again (interface not re-exported, no breaking consumer); documented Python parity deviation; added DD=72 checksum-valid vector
  • R4 external round 3: removed dead try/catch, added typeof guard to parse(), made idType required, fixed leap-year test to isolate path, added DD=32 gap test, added 1900s-century D-nummer test vector

Final verdict: APPROVED (Test Coverage 10/10, Code Quality 9/10).

AngusHsu added 7 commits April 5, 2026 10:40
…upport

- Extend NationalIdParseResult with idType discriminator ('fodselsnummer' | 'd-nummer')
- Update parse() to detect D-nummer (DD 41-71): subtract 40 from day field, set idType
- Add comprehensive test file covering all three milestone v1.6.0 issues:
  - 14 fødselsnummer validation tests (valid/invalid, century logic, date edge cases)
  - 8 D-nummer validation tests (valid, boundary negatives, differentiation)
  - 5 checksum tests (valid/invalid first+second check digits, malformed input)
  - 11 parse() function tests (birth date, gender, idType, checksum field, errors)
  - 5 integration tests (validateNationalId, parseIdInfo via public API)
- Remove redundant `as const` from idType literal assignment
- Extract mmNum to avoid double parseInt(mm, 10) in date construction/validation
- Shorten D-nummer detection comment to non-obvious why only
- Import NationalIdParseResult in test for clean type assertion
- Fix misleading test.each label for 29029600013: individual 000 (not 001), gender FEMALE (not male)
- Add 1800s century test vector (10107055008): individual 550, yy=70>=54 → century 18
- Fix DD=40 test: clarify isDNummer=false (40<41), use checksum-valid vector
  40019000119 to isolate date-overflow rejection path from checksum rejection
- Remove duplicate standalone leap-year test (already covered in test.each table)
- Make idType field optional on NationalIdParseResult to avoid breaking change
  for downstream consumers constructing the type literally; parse() always
  populates it
- Document Python parity deviation in source comment and CHANGELOG: D-nummer
  support is a TypeScript-side extension required by issue #29 acceptance
  criteria; checksum and other behaviors remain identical to Python source
- Fix DD=72 test: clarify isDNummer=false (72>71), use checksum-valid vector
  72019000290 to isolate date-overflow rejection from checksum rejection
- Remove dead try/catch in parse(): JS Date never throws on overflow; date
  validity is already enforced by getFullYear/getMonth/getDate comparisons
- Add typeof string guard at top of parse() to mirror validate() and prevent
  parse() from accidentally accepting non-string inputs whose toString()
  returns 11 valid digits
- Make idType field required (drop optional ?): NationalIdParseResult is not
  re-exported via the country barrel, so no external consumer constructs it
- Fix Feb 29 1999 leap-year reject test: assert parse() returns null directly
  to isolate the date-validation path from checksum validation
- Add DD=32 gap regression test (32019000035, checksum-valid) covering the
  range 32-40 between fødselsnummer max (31) and D-nummer min (41)
- Add 1900s D-nummer test vector 45055590110 covering interaction between
  D-nummer day adjustment and individual 900-999/yy>=40 century branch
@AngusHsu AngusHsu merged commit 526b817 into main Apr 7, 2026
7 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.

test(nor): Add Norway fødselsnummer validation tests

1 participant