Use privileged controls to arrange or inspect one world without replacing the provider-shaped application interaction under test.
Every plugin exposes provider-shaped routes for the application and privileged operations for local control. Both reach one service world, but they provide different evidence.
This checked test arranges Grace through an operation, then proves the application-facing
users.list route sees that exact state:
import { createTestRuntime } from "localhost2137/testing";
import { expect, it } from "vitest";
import config from "../localhost.config.js";
it("arranges a user with an operation and reads it through Slack-shaped HTTP", async () => {
const runtime = await createTestRuntime({
config,
port: 0,
storage: "temporary",
});
try {
const instance = await runtime.createInstance({ seed: true });
try {
const grace = await instance.slack.createUser({ name: "Grace" });
const response = await fetch(new URL("users.list", instance.slack.connection.apiUrl), {
headers: {
authorization: `Bearer ${instance.slack.connection.botToken}`,
},
});
expect(response.status).toBe(200);
await expect(response.json()).resolves.toMatchObject({
members: [
{ id: "U000000", name: "localhost2137-bot" },
{ id: grace.id, name: "Grace" },
{ id: "U_ADA", name: "Ada" },
],
ok: true,
});
} finally {
await instance.destroy();
}
} finally {
await runtime.close();
}
});pnpm exec vitest run test/owned-runtime.test.tsThe operation is honest setup because the claim is about reading an existing user through the
Slack-shaped route. The test proves route selection, bearer authentication, and response shape over
operation-created state; its direct test-side fetch does not prove an application adapter. Merely
asserting the result of createUser would skip all provider HTTP evidence.
Change the claim and the right boundary can change. A plugin semantic test may intentionally claim
that createUser validates and persists one user; there, the operation itself is the subject.
| Scenario job | Usual boundary | Evidence produced |
|---|---|---|
| Arrange users, catalog entries, accounts, or failures | Typed operation or generated CLI | The plugin accepted privileged setup into this world. |
| Trigger an external actor, event, or time transition | Operation or runtime clock | The local actor or runtime capability ran. |
| Exercise application integration | Application SDK, HTTP client, or callback handler | The application crossed the documented provider-shaped surface. |
| Inspect emulator state | Inspection operation or supported provider read | The selected world reached the observed state. |
| Observe application behavior | Application output, database, queue, or public effect | The application produced its claimed result. |
Operations weaken evidence only when they replace the application interaction named by the claim. They are valid for setup, triggers, inspection, and direct operation-contract tests.
The Slack ping-pong example assigns four boundaries deliberately:
| Code in the checked example | Role |
|---|---|
instance.slack.createUser, createChannel, addUserToChannel | Arrange a workspace through control operations. |
instance.slack.sendMessage({ text: "ping" }) | Represent an external member and trigger the plugin's signed event. |
Bolt app.message(/^ping$/, ...) and say("pong") | Receive provider-shaped HTTP and reply through the application's normal SDK. |
instance.slack.listMessages(...) | Inspect the shared workspace after tracked delivery settles. |
Calling the Bolt handler directly could be a valid handler unit test, but would skip HTTP parsing and
signature validation. Writing pong through another operation would skip the outbound SDK request.
Callbacks and webhooks contains the complete checked loop.
Provider APIs often cannot create arbitrary surrounding state, force a payment outcome, or move 30 days forward. Adding invented provider routes for those jobs makes application traffic less compatible. Operations expose local-only control explicitly through one plugin-owned Zod schema.
The same operation is available through:
pnpm exec localhost describe slack --instance dev --json
pnpm exec localhost exec slack create-user --instance dev --name Grace --jsonTyped tests call instance.slack.createUser({ name: "Grace" }); the CLI generates flags from the same
installed schema. Operation success proves that control surface, not provider compatibility.
Provider routes and operations should be adapters over the plugin's same domain behavior and persistence. Separate implementations can accept different rules while appearing to share a database. Provider response mapping belongs at the route boundary; CLI and control error mapping belong at the operation boundary.
That separation lets semantic tests compare the resulting state without pretending the interfaces are interchangeable. The plugin still needs distinct evidence for:
The recurring-billing example creates a product and price through operations, then creates a customer and subscription and lists invoices through the official Stripe SDK. That proves the SDK path the application uses. A lower-level plugin test about renewal transactions can inspect invoices through an operation without weakening its different claim.
State does not decide the evidence; the falsifiable claim does. Continue with Integration testing for ownership, Using plugins for the installed control inventory, or Provider compatibility for what provider-shaped success actually establishes.