Testing API reference
Complete reference for Ownfold compliance suites, in-memory adapters, transports, storage, lifecycle channels, encrypted-record repositories, and corruption helpers.
@ownfold/testing contains contract-compliant in-memory implementations and adversarial helpers.
Use it in tests only.
pnpm add -D @ownfold/testing@beta
Export map
| Export | Implements or returns | Primary use |
|---|---|---|
checkVaultAdapterCompliance |
Compliance result | Verify lifecycle adapter semantics. |
checkVaultRotationAdapterCompliance |
Compliance result | Verify rotation transactions and conflicts. |
InMemoryVaultAdapter |
VaultAdapter & VaultRotationAdapter |
Server unit/integration tests without a database. |
InMemoryVaultTransport |
VaultTransport & VaultRotationTransport |
Browser lifecycle tests without HTTP. |
createInMemoryVaultTransport |
InMemoryVaultTransport |
Concise transport construction. |
MemoryVaultStorage |
VaultStorage |
Browser client tests without IndexedDB. |
MemoryVaultLifecycleChannelHub |
Channel factory and message recorder | Multi-tab lifecycle tests. |
InMemoryEncryptedRecordRepository |
RotationRecordStore |
Key-rotation tests. |
corruptBase64Url |
string |
Authentication/corruption negative tests. |
Adapter compliance
checkVaultAdapterCompliance(adapter)
Runs the required vault, recovery, device, and pairing behavior against the supplied adapter. The suite checks successful operations, same-identity idempotency, conflicting identity, ownership scope, terminal state, and stale revision rejection.
import { checkVaultAdapterCompliance } from "@ownfold/testing"
it("implements the lifecycle contract", async () => {
const result = await checkVaultAdapterCompliance(createRealAdapter())
expect(result.status).toBe("ok")
})
checkVaultRotationAdapterCompliance(adapter)
Checks begin, progress, stale checkpoint, completion, active-device coverage, atomic cutover, and idempotent completion behavior.
const result = await checkVaultRotationAdapterCompliance(adapter)
if (result.status === "error") throw result.error
Run both suites against a newly created disposable database for each test. The suite owns its fixed identifiers and expects no pre-existing rows. A passing in-memory run does not replace real driver, transaction, migration, and concurrency tests.
InMemoryVaultAdapter
InMemoryVaultAdapter: No interface or type named "InMemoryVaultAdapter" found.The adapter holds validated records in process memory and implements official conflict semantics. Use a fresh instance per test to avoid state leakage.
const adapter = new InMemoryVaultAdapter()
const server = createVaultServer({
adapter,
getUserId: async () => "test-user",
})
It is not a production fallback: process exit loses data, instances do not coordinate, and memory does not model database isolation.
InMemoryVaultTransport
InMemoryVaultTransport: No interface or type named "InMemoryVaultTransport" found.The transport stores remote-safe lifecycle values and records observed operation names. It is useful
for testing VaultClient without a server route.
const transport = createInMemoryVaultTransport()
const client = createVaultClient({
transport,
storage: new MemoryVaultStorage(),
cryptoEngine: testCryptoEngine,
})
MemoryTransportOperation is the union of observable operation names from getVault through
completeRotation. Assert on those names when verifying that invalid local state fails before a
network call.
MemoryVaultStorage
MemoryVaultStorage: No interface or type named "MemoryVaultStorage" found.It implements main local vault state plus pending pairing, Recovery Kit replacement, and rotation
records. clear() removes main local state; each pending workflow has its own clear method, matching
the production storage contract.
Use a new instance per browser profile. Sharing one instance models reload in the same profile; separate instances model separate devices.
MemoryVaultLifecycleChannelHub
const hub = new MemoryVaultLifecycleChannelHub()
const first = createVaultClient({
transport,
lifecycleChannelFactory: hub.createChannel,
})
const second = createVaultClient({
transport,
lifecycleChannelFactory: hub.createChannel,
})
createChannel supplies same-name in-memory channels. observedMessages() returns all posted
VaultLifecycleMessage values in order, allowing tests to assert cross-client lock and state-change
propagation without BroadcastChannel.
Rotation record repository
InMemoryEncryptedRecordRepository implements the browser RotationRecordStore. Seed it with
encrypted records, pass it to rewrapRotationRecords or useKeyRotation, and inspect the updated
envelopes after each batch.
InMemoryEncryptedRecordRepository: No interface or type named "InMemoryEncryptedRecordRepository" found.Test interruption by stopping after a checkpoint, constructing a new client with the same storage and repository, and resuming. Assert that processed records are not duplicated or skipped.
Corruption helper
corruptBase64Url(value) changes a non-empty base64url value while keeping it structurally valid.
Use it to test authenticated failure rather than parser failure.
const corrupted = {
...envelope,
ciphertext: corruptBase64Url(envelope.ciphertext),
}
const result = await client.decryptJson({
...context,
payload: corrupted,
})
expect(result.status).toBe("error")
For structural validation tests, supply malformed or unsupported-version data directly instead.
Recommended test matrix
| Boundary | Minimum cases |
|---|---|
| Authentication | Valid, anonymous, expired, malformed session, provider failure |
| Adapter | Compliance suites, driver failure, corrupted stored JSON, concurrent stale writes |
| Transport | Oversized/malformed response, non-success code, network failure, duplicate delivery |
| Browser lifecycle | Create, verify, reload, lock, unlock, restore, unavailable device |
| Records | JSON/binary round trip, wrong owner/namespace/record ID, corrupted ciphertext |
| Pairing | Approve, cancel, expiry, stale revision, mismatched device envelope |
| Rotation | Empty and multi-batch sets, interruption, resume, stale checkpoint, atomic completion |
| Multi-tab | Remote lock, disposal, channel isolation, missed/duplicate notification tolerance |
Production exclusion
Keep this package in devDependencies. Do not export test instances from application modules or
bundle deterministic crypto/test storage into production. Production smoke tests should use packed
public packages with real database and transport boundaries.
See Database adapters, contracts, and the complete export index.