Skip to content
Ownfold
Esc
navigateopen⌘Jpreview
On this page

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

Could not generate a type table for 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

Could not generate a type table for 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

Could not generate a type table for 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.

Could not generate a type table for 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.

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.

Last updated on August 4, 2026

Was this page helpful?