Adapter certification
An interface is a promise. Certification is how an adapter proves it kept it, in a form someone else can check.
Profiles
A profile is a named set of checks written against an interface, not an implementation: cacheStoreCertificationProfile, jobStoreCertificationProfile, storageAdapterCertificationProfile, runtimeAdapterCertificationProfile, and eventDeliveryCertificationProfile. Domain-specific seams define their own with defineCertificationProfile, which validates the profile at module load, so a check naming an undeclared capability fails before any adapter runs.
Certify an adapter
// doc-check: skip - createRedisCache stands in for the adapter under test
import { test } from "bun:test"
import {
assertAdapterCertification,
cacheStoreCertificationProfile,
certifyAdapter,
} from "@nifrajs/testing/certification"
test("the Redis cache adapter satisfies the cache contract", async () => {
const report = await certifyAdapter({
profile: cacheStoreCertificationProfile(),
adapterId: "redis-cache",
// A FRESH adapter per check, so one failure cannot contaminate the next.
createAdapter: () => createRedisCache(url),
cleanup: (adapter) => adapter.clear(),
})
assertAdapterCertification(report) // throws, naming every failed check
})Profiles are structural and dependency-free. A third-party adapter can certify itself in its own test suite without adopting anything else, which is the point: trust becomes portable rather than something we vouch for.
The report is the evidence
{
"schemaVersion": 1,
"ok": true,
"profile": { "id": "cache-store", "version": 1 },
"adapterId": "redis-cache",
"capabilities": [
{ "capability": "read-write", "status": "passed", "checks": ["set-get", "delete"] },
{ "capability": "tag-invalidation", "status": "passed", "checks": ["invalidate-tag"] }
],
"evidenceHash": "9f2c..."
}The capability matrix is the useful artifact. It says what an adapter supports, per capability, with the checks that back each one. verifyAdapterCertification recomputes evidenceHash, so a stored report can be checked rather than believed.
Failures carry a class, not a message
A failed check records the error class only. Provider messages routinely carry connection strings, tokens, and payload fragments, and evidence is meant to be shared, so it never becomes a place credentials leak. Cleanup errors cannot turn a failed functional check into a pass.