diff --git a/README.md b/README.md index c02d4a0..d0a8def 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ await didit.sessions.create( Resources and methods follow the API: `didit..(...)`, with nested resources such as `didit.sessions.imports` or `didit.cases.notes`. Path parameters are positional; everything else goes in one params object whose fields keep the API's names (snake_case). -[api.md](api.md) lists all 257 methods with their HTTP routes and types. +[api.md](api.md) lists every method with its HTTP route and types, generated from `openapi/`. ```ts import { fileFromPath } from "@didit-protocol/sdk-node"; diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 32996e0..a3d837c 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -87,7 +87,7 @@ Convert names to the language's idiom and nothing else: | Ruby | `didit.sessions.imports.create(...)` | | C# | `await client.Sessions.Imports.CreateAsync(...)` | -The full resource tree (version 1, 248 server operations in 47 resources) is in `overlay.json`. +The full version-1 resource tree is generated from `openapi/*.json` and listed in `api.md`, one section per resource; `overlay.json` holds only the naming overrides upstream does not carry yet. Highlights: `sessions`, `sessions.imports`, `sessions.reviews`, the standalone APIs (`id_verification.verify`, `face_match.compare`, `face_search.search`, `passive_liveness.check`, `age_estimation.estimate`, `proof_of_address.verify`, `document_ai.analyze`, `aml.screen`, `database_validation.validate`, `phone.send_code`, `email.check_code`, `ip.assess_risk`, `kyb.search`), `id_wallets`, `fraud_checks`, `users`, `businesses`, `transactions`, `transactions.rules`, `transactions.travel_rule`, `wallet_screening`, `travel_rule`, `travel_rule.wallet_addresses`, `workflows`, `questionnaires`, `webhooks.destinations`, `billing`, `biometric_templates`, `customization`, `cases` (with `notes`, `links`, `checklist`, `requests`, `teams`, `routing_rules`, `blueprints`, `regulatory_reports`), `regulatory_reports`, `report_templates`, `networks`, and `account` (`account.organizations`, `account.applications`). ## 5. Architecture diff --git a/tests/docs.test.ts b/tests/docs.test.ts new file mode 100644 index 0000000..16336ab --- /dev/null +++ b/tests/docs.test.ts @@ -0,0 +1,52 @@ +// The generated API reference and the README that points at it. The number of +// methods in api.md's header must match the method rows below it, and a method +// count stated in the README must match it too, so the docs cannot drift from +// the generated reference as the API grows. + +import { readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +const readme = readFileSync(new URL("../README.md", import.meta.url), "utf8"); +const api = readFileSync(new URL("../api.md", import.meta.url), "utf8"); +const design = readFileSync(new URL("../docs/DESIGN.md", import.meta.url), "utf8"); + +/** The count in "Every method of ...: N methods, generated from openapi/". */ +const declared = /Every method of `@didit-protocol\/sdk-node`: (\d+) methods/.exec(api)?.[1]; + +/** One row per method in the api.md tables. */ +const rows = api.split("\n").filter((line) => line.startsWith("| `didit.")); + +/** One section per resource in api.md. */ +const sections = api.split("\n").filter((line) => line.startsWith("## `didit.")); + +describe("API reference", () => { + it("declares the number of methods it lists", () => { + expect(declared).toBeDefined(); + expect(Number(declared)).toBe(rows.length); + }); + + it("is linked from the README", () => { + expect(readme).toContain("[api.md](api.md)"); + }); + + it("does not state a method count the reference disagrees with", () => { + // The README deliberately names no count: the spec sync regenerates api.md + // and the tables above, so a number copied into the README goes stale. + // If one is added, it has to be right. + const claimed = /(\d+) methods/.exec(readme)?.[1]; + if (claimed !== undefined) expect(Number(claimed)).toBe(rows.length); + }); +}); + +describe("Design document", () => { + it("does not state server operation or resource counts the reference disagrees with", () => { + // DESIGN.md's resource-tree paragraph is where a reader is told how large + // the contract is. It deliberately names no size, because a spec sync + // regenerates api.md; if a size is added back, it has to be right. + const claimed = /(\d+) server operations in (\d+) resources/.exec(design); + if (claimed !== null) { + expect(Number(claimed[1])).toBe(rows.length); + expect(Number(claimed[2])).toBe(sections.length); + } + }); +});