JOLT DATA SDK GUIDE · 07

Test without a daemon

An App Definition provides an in-memory test interface with the same typed Resources and Items as App.connect(). Tests can exercise application rules without starting Jolt Console, approving a session, or waiting for a network.

Test one identity

Use App.test() for isolated application behavior. Each call starts with fresh state, even when it uses the same identity string.

src/task-list.test.tsts
import { describe, expect, it } from "vitest";
import { State } from "jolt-sdk/data";
import { TaskList } from "./mutations";

describe("one identity", () => {
  it("uses the normal typed Resource interface", async () => {
    const app = TaskList.test({ identity: "alice.jolt" });
    const created = await app.tasks.create({ title: "Write a test", done: false });
    const updated = await created.update({ done: true });

    expect(updated.value).toEqual({ title: "Write a test", done: true });
  });

  it("starts each test instance with fresh state", async () => {
    const first = TaskList.test({ identity: "alice.jolt" });
    const created = await first.tasks.create({ title: "Temporary", done: false });
    const second = TaskList.test({ identity: "alice.jolt" });

    expect((await second.tasks.get(created.ref)).state).toBe(State.Missing);
  });
});

Schemas, migrations, access-shaped methods, immutable Items, lifecycle states, and conflict policies all remain active. Test through app.tasks or another generated Resource instead of arranging data through a separate fixture API.

Test Alice and Bob

Use App.testWorld() when identities should share deterministic state. Views from world.as(identity) represent application users in one synchronized test world.

src/feed.test.tsts
import { describe, expect, it } from "vitest";
import { Feed } from "./subscriptions";

describe("two identities", () => {
  it("shares deterministic public data between Alice and Bob", async () => {
    const world = Feed.testWorld();
    const alice = world.as("alice.jolt");
    const bob = world.as("bob.jolt");
    const post = await alice.posts.create({
      text: "Hello Bob!",
      postedAt: new Date("2026-08-31T12:00:00.000Z"),
    });

    const fromAlice = await bob.posts.for("alice.jolt").get(post.ref);

    expect(fromAlice.isPresent()).toBe(true);
    if (!fromAlice.isPresent()) throw new Error("Expected Alice's post");
    expect(fromAlice.value.text).toBe("Hello Bob!");
    expect("update" in fromAlice).toBe(false);
  });
});

Bob can read Alice's post because the Collection declares Read.AnyIdentity. His remote view remains read-only, exactly as it does in a connected application.

Model separate installations only when needed

world.as(identity) is the simple choice for application journeys. It does not model offline installations.

When a concurrency test genuinely needs independent copies, use world.device(identity, name). Named devices keep separate histories until the test calls world.sync(). The advanced Manual conflicts guide shows that shape with a workstation and laptop.

Do not introduce devices and synchronization into ordinary component or domain tests. They exist to test concurrent-edit policy, not as general fixture setup.

Know what the test interface proves

The in-memory interface is good for:

It does not prove Jolt Console approval, daemon feature compatibility, capability enforcement or revocation, durable restart behavior, provider discovery, or real multi-node networking. Keep a small real-daemon test for those boundaries, as Jolt does for Chirp and Spoke.

Run the tests

The guide examples use Vitest:

yarn vitest run

No special Jolt test runner or fixture language is required.

Continue learning