Skip to main content

Interop and SDK tests

Two suites test Certichain from the outside. The interop suite checks the exported documents with software the project did not write, so a format bug cannot hide behind the project's own parser. The SDK suite checks that @certichain/sdk accepts only answers that are authentic, fresh and from the expected deployment.

Interop (interop/)​

The folder is test only: nothing in it is imported by the app, and it has its own package.json and lock file. Its runtime dependencies are pinned exactly:

PackageVersionRole
@digitalbazaar/vc7.3.0The verifiable credential verifier
@digitalbazaar/data-integrity2.5.0Data integrity proofs, used with the Certichain plugin
@digitalbazaar/eddsa-rdfc-2022-cryptosuite1.3.0The stock suite for the standard Open Badge proof
@digitalbazaar/ed25519-multikey1.3.1Ed25519 keys
@digitalbazaar/vc-bitstring-status-list2.0.1Reads status list bits
canonicalize2.1.0JSON canonicalisation
ethers6.14.4EIP-712 signature recovery in the plugin

How it works​

  1. npm run interop:generate runs interop/generate.ts from the repository root with vite-node. It builds the fixtures with the app's real exporters and public development keys.
  2. CI then runs git diff --exit-code -- interop/fixtures, so a change in an exporter that alters the output must be committed deliberately.
  3. npm run interop (npm --prefix interop test) runs node --test over interop/test/*.test.mjs. The tests read only fixtures/ and never import from src/.

The document loader in interop/lib/loader.mjs serves the vendored JSON-LD contexts and resolves did:key and did:pkh itself, and refuses every other address, so no document can make the library touch the network.

The plugin in interop/plugin/ (certichain-eip712-2026.mjs, checks.mjs, typed-data.mjs) teaches the library the Certichain proof type, which reuses the issuer's EIP-712 signature. A pass means the signature is valid for the issuer address in the document, every visible claim is derived from the signed fields, and shown details match the signed hashes. It does not mean the issuer is registered or the credential is not revoked: those live on the chain.

What each file checks​

6 files, 44 tests (counted 2026-10-24).

FileTestsChecks
test/certichain-plugin.test.mjs13A Certichain VC verifies and reports registration as unchecked; a VC with no details reports them as unproven; changed name, student ID, programme, award, record type, issuer, id or date fail; a wrong subject salt, another signer, an altered signature, a did:pkh of another chain and a non-canonical proof value fail; not valid before validFrom; an Open Badge with the Certichain proof verifies and a changed signed claim fails; the plugin does not see a changed achievement name (only the Certichain parser and the standard proof do); a status list proof is refused when no publisher is pinned
test/standard-proof.test.mjs8An Open Badge with the standard eddsa-rdfc-2022 proof verifies with the stock library and no plugin, without the network; any changed field, a removed or changed proof, changed proof options and an undefined term fail; a re-signed badge also passes (a stock verifier learns only that a key signed, not whose key); not valid before validFrom
test/status-list.test.mjs14Revocation and suspension bits read correctly, without the network; lists signed by another key, expired, edited after signing, with a moved end date, served at another address, served for the other purpose, or with no or the wrong pinned publisher are refused; three tests record the library's defaults a Certichain verifier must change
test/api-answers.test.mjs6A client written from the API description accepts signed answers, checks the nonce echo, refuses any changed answer and any other signer, calls an old answer stale, and finds no holder data in answers
test/loader.test.mjs2The loader refuses unknown addresses and serves only vendored contexts and its own DID resolution
test/fixtures.test.mjs1The fixtures match fixtures.sha256 exactly, with nothing missing or extra
Findings the suite records

The status list tests document three things a verifier using the stock library must know: it expects the credential's issuer to publish the lists, so Certichain lists need that check turned off and the publisher pinned instead; it uses the real clock when it verifies a list itself; and the status entry's index is not covered by the issuer's Certichain proof, so a moved index reads a different bit. The plain Certichain badge proof also does not cover the achievement name. The full write-up is in docs/interop/report.md.

Run​

npm ci
npm --prefix interop ci
npm run interop:generate
git diff --exit-code -- interop/fixtures
npm run interop

SDK (packages/sdk/test/)​

@certichain/sdk (version 0.1.0, private, unpublished) is a client for the verification API. It returns data only after it has checked the answer's EIP-712 signature against a signer the caller pinned, the echoed nonce, the freshness window, the data age and the deployment. Its runtime dependencies are exactly canonicalize and ethers. See SDK.

The tests run with Vitest, both inside the root run and alone with npm run sdk:test (vitest run packages/sdk). The package is built with npm run sdk:build (TypeScript only).

FileWhat it proves
boundaries.test.tsThe package stands on its own: it imports nothing from the app, is private, has two exact-pinned runtime dependencies and no install scripts
envelope.test.tsAnswers produced by the app's own handlers (shared fixtures) are accepted; the deployment, time window, nonce, hostile input and junk are each checked
client.test.tscreateClient, getCredential, verifyFile and getStatus; API and network errors; the API key; running where fetch must keep its receiver; evidence; options that would disable a check are refused; server error text; a base URL with a path
standing.test.tscredentialStanding and fileStanding, the good-standing rules over a checked answer
openapi-binding.test.tsThe SDK's types follow the OpenAPI document served by the app
example.test.tsThe sample app in packages/sdk/examples/verify-credential.mjs, run against the real API handlers on a local HTTP server

packages/sdk/test/support/ holds the signer, a local server and two file verdict fixtures. The E2E journey e2e/journeys/verification-api.journey.ts also runs the SDK against the app.

In CI​

The Interop job runs the generate, diff and test steps above; the SDK job installs the package, builds it and runs npm run sdk:test. See Continuous integration.

Source files​

  • interop/README.md, interop/package.json, interop/generate.ts
  • interop/lib/loader.mjs, fixtures.mjs, verify.mjs, status.mjs, api-client.mjs
  • interop/plugin/certichain-eip712-2026.mjs, checks.mjs, typed-data.mjs
  • interop/fixtures/, interop/test/
  • packages/sdk/package.json, packages/sdk/src/, packages/sdk/test/, packages/sdk/examples/verify-credential.mjs
  • vc/cryptosuite-certichain-eip712-2026.md (the proof type's description)