@foundryprotocol/0gkit-testing
v1.1.0
Published
Test toolkit for 0gkit: deterministic mock providers for storage/compute/DA, signed fixture attestations + receipts, an HD-derived testWallet, a setupLocalDevnet() vitest helper that wraps `0g dev`, and four vitest matchers (toBeConfirmedOn0G, toHaveRootM
Maintainers
Readme
@foundryprotocol/0gkit-testing
Test toolkit for 0gkit. Mock providers, fixture factories, a deterministic
testWallet, a setupLocalDevnet() vitest helper, and four vitest matchers
that understand 0G semantics.
npm i -D @foundryprotocol/0gkit-testingMocks — sub-millisecond primitives
import {
mockStorageClient,
mockComputeClient,
mockDAClient,
} from "@foundryprotocol/0gkit-testing";
const storage = mockStorageClient();
const { root, tx } = await storage.upload(new TextEncoder().encode("gm"));
const bytes = await storage.download(root); // round-trips deterministically
const est = await storage.estimate(new Uint8Array(300_000));
//=> { kind: "storage", gas, fee, breakdown: { sizeBytes: 300_000, segments: 2 } }
const dr = await storage.upload(bytes, { dryRun: true });
//=> { dryRun: true, estimate, result: { root, tx: { latencyMs: 0 }, raw } }
const compute = mockComputeClient(); // default echo responder
const reply = await compute.inference({
messages: [{ role: "user", content: "ping" }],
});
//=> { output: "echo: ping", receipt, raw }
const ce = await compute.estimate({
messages: [{ role: "user", content: "ping" }],
});
//=> { kind: "compute", gas: 0n, fee, breakdown: { inputTokens, outputTokensMax, model } }
const da = mockDAClient();
const { digest } = await da.publish(bytes);
const ok = await da.verify(digest, bytes); // true; tamper bytes → falseSame upload / download / inference / estimate / publish shape as the
real packages — including the SP6 inference() API on Compute and the SP7
{ dryRun: true } overloads on Storage and Compute (returning the
DryRunResult<T> envelope from @foundryprotocol/0gkit-core). Roots and
digests are sha256 of the input bytes — deterministic, so tests assert on
stable values without snapshots.
Fixtures
import {
fixtureReceipt,
fixtureAttestation,
FIXTURE_ATTESTATION_SIGNER,
} from "@foundryprotocol/0gkit-testing";
const tx = fixtureReceipt({ blockNumber: 17n });
// { txHash: "0xab...ab", blockNumber: 17n, latencyMs: 5 }
const signed = await fixtureAttestation({ scores: [0.9, 0.85] });
// signed.envelope, signed.digest, signed.signature — verifies with
// @foundryprotocol/0gkit-attestation.verifyEnvelope(signed, FIXTURE_ATTESTATION_SIGNER)testWallet — deterministic Signer
import { testWallet } from "@foundryprotocol/0gkit-testing";
const signer = testWallet({ index: 0 });
// Matches anvil's pre-funded dev account 0:
// address = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266Uses the same mnemonic as 0gkit-devnet's pre-funded accounts, so any test
hitting setupLocalDevnet() with testWallet({ index: 0 }) has gas
immediately — no faucet round-trip.
setupLocalDevnet({ autoStart })
// vitest.config.ts
import { setupLocalDevnet } from "@foundryprotocol/0gkit-testing";
export default {
test: {
globalSetup: ["./vitest.setup.ts"],
},
};
// vitest.setup.ts
import { setupLocalDevnet } from "@foundryprotocol/0gkit-testing";
export default async function () {
const devnet = await setupLocalDevnet({ autoStart: true });
return () => devnet.stop();
}Per-suite is equally easy:
import { beforeAll, afterAll } from "vitest";
import { setupLocalDevnet } from "@foundryprotocol/0gkit-testing";
const devnet = await setupLocalDevnet();
beforeAll(() => devnet.start());
afterAll(() => devnet.stop());Lazily imports @foundryprotocol/0gkit-devnet so this package stays light
when devnet isn't needed.
Vitest matchers
// vitest.setup.ts
import "@foundryprotocol/0gkit-testing/matchers";expect(receipt).toBeConfirmedOn0G();
expect(root).toHaveRootMatching(/^0xab/);
await expect(signed).toBeValidAttestation(FIXTURE_ATTESTATION_SIGNER);
expect(err).toBeZeroGError("CONFIG");Failure messages name what was expected, what was received, and (when possible) a one-line hint pointing at the likely cause.
| Matcher | Asserts |
| --------------------------------------- | --------------------------------------------------------------------------------------------- |
| toBeConfirmedOn0G() | Receipt has hex txHash, positive bigint blockNumber, non-negative latencyMs. |
| toHaveRootMatching(regex) | Value is a 32-byte hex root that matches the regex. |
| toBeValidAttestation(expectedSigner?) | Digest is intact and the signature recovers (optionally to the expected signer). |
| toBeZeroGError(code) | Error is a ZeroGError with the named code (CONFIG / NETWORK / CHAIN / ATTESTATION). |
Tree-shaking sub-paths
Cherry-pick what you need to keep the test bundle small:
import { mockStorageClient } from "@foundryprotocol/0gkit-testing/mocks";
import { fixtureReceipt } from "@foundryprotocol/0gkit-testing/fixtures";Notes
0gkit-attestationis an optional runtime dep —fixtureAttestationandtoBeValidAttestationlazily import it. Install it alongside if you use those features.setupLocalDevnetrequires@foundryprotocol/0gkit-devnet(declared as a devDep here; install it where you use it).
License
MIT
