← API Contract LabDemonstration case studyTest the boundary.
Document the exception.
A valid example proves very little about rejection behavior. This experiment makes the rules, boundary cases, and parser limitations visible.
22documented fixtures
22/22match expected decisions
30automated tests passed in the recorded run
The question
Can a small request validator distinguish a format error from a contract error, reject unintended coercion, and make its own limits clear? The input is an order-shaped JSON object containing customerId, amountCents, and currency. The experiment has no database, payment processor, or customer records.
A three-step walkthrough
- Parse a request.
A trailing comma fails parsing. An array parses successfully but fails the required object shape. A resource limit can stop processing before parsing even starts.
- Check the contract.
The string "1250" is rejected as an amount. The number 1250 passes. Tests also cover missing fields, exact length boundaries, supported currencies, and unexpected properties.
- Inspect what the parser already changed.
Duplicate keys and rounded numerical values show why an accepted request is not proof that the original JSON text was unambiguous.
Run these examples in the lab
The implementation choices
The function uses JSON.parse, checks the top-level shape, inspects each required field, rejects extra own properties, and returns a reconstructed object only after all checks pass. It does not trim identifiers or convert strings into numbers. The result field named normalized is a serialization of accepted values, not a promise of canonical JSON or normalized identity.
Input and identifier limits count JavaScript UTF-16 code units: 131,072 and 128 respectively. They are not byte limits or user-perceived character counts. The amount check applies Number.isSafeInteger to the parsed value and requires it to be positive. A production pricing system would need its own much narrower limits.
Recorded results
The Node.js 24.19.0 run passed 30 tests: six original regression tests, 22 fixture checks, and two checks documenting parser limitations. These are deterministic correctness checks, not a performance benchmark, production certification, or evidence that every possible input is covered. The table below runs the same fixture inputs through the validator when this page is rendered.
Expected and observed decisions for the 22 request fixtures| Fixture | Expected | Observed | What it checks |
|---|
| Documented request | Accept | Accept | All three fields satisfy the published rules. |
|---|
| Number supplied as a string | Reject | Reject | The contract does not convert strings to numbers. |
|---|
| Fractional cents | Reject | Reject | The parsed amount must be an integer. |
|---|
| Zero amount | Reject | Reject | This example requires a positive amount. |
|---|
| Negative amount | Reject | Reject | Refund semantics are outside this order contract. |
|---|
| Maximum safe integer | Accept | Accept | This is the allowed numeric boundary, not a realistic pricing limit. |
|---|
| Integer above safe range | Reject | Reject | The parsed value lies outside JavaScript's safe integer range. |
|---|
| Whitespace-only customer ID | Reject | Reject | A whitespace-only identifier is empty for this contract. |
|---|
| Missing customer ID | Reject | Reject | All three fields are required. |
|---|
| Customer ID at 128 code units | Accept | Accept | The implementation measures JavaScript UTF-16 string length. |
|---|
| Customer ID at 129 code units | Reject | Reject | One unit beyond the documented limit is rejected. |
|---|
| Unexpected admin property | Reject | Reject | Only the three documented own properties are accepted. |
|---|
| Unsupported currency | Reject | Reject | The supported set is deliberately narrow. |
|---|
| Lowercase currency | Reject | Reject | Currency codes are case-sensitive here. |
|---|
| Top-level array | Reject | Reject | Valid JSON does not imply an order-shaped object. |
|---|
| Trailing comma | Reject | Reject | Parsing fails before contract checks can run. |
|---|
| Supported GBP request | Accept | Accept | GBP is in the documented supported set. |
|---|
| Supported EUR request | Accept | Accept | EUR is in the documented supported set. |
|---|
| Unexpected __proto__ property | Reject | Reject | Parsed input is inspected and reconstructed, not merged into another object. |
|---|
| Top-level null | Reject | Reject | Null parses successfully but is not an order object. |
|---|
| Input at 131,072 code units | Accept | Accept | JSON permits trailing whitespace; the length boundary is inclusive. |
|---|
| Input at 131,073 code units | Reject | Reject | The resource gate stops validation before parsing; syntaxValid false here does not prove malformed JSON. |
|---|
Two accepted inputs that deserve attention
{"customerId":"demo","amountCents":0,"amountCents":1,"currency":"USD"}
This parser keeps the final amountCents value, so the contract accepts 1. The validator does not detect duplicate keys. If that ambiguity matters, use a parser or input contract that rejects it before ordinary object construction.
{"customerId":"demo","amountCents":1.00000000000000001,"currency":"USD"}
JavaScript parses this amount as 1. Number.isSafeInteger cannot recover precision that has already been lost. Applications requiring exact decimal text need an explicit representation and parser appropriate to that requirement.
Reproduce the experiment
Download or clone the source repository, use Node.js 22.6 or later, and run:
node --experimental-strip-types --test contract.test.mjs contract-cases.test.mjs
node --experimental-strip-types report.mjs
The report command writes contract-report.json with each expected and observed result, the runtime version, and UTC timestamp. The test command also verifies issue messages and documents the two parser limitations.
Source and tests on GitHub ↗ · Download the recorded fixture results
What a passing result cannot establish
This demonstration was created in September 2026 with AI assistance. It is not historical client work. No real orders were processed. Passing does not prove customer identity, authorization, correct pricing, safety under concurrency, duplicate-request handling, or readiness for production. Server-side enforcement and domain-specific checks remain separate engineering work.