Skip to content

Testing and troubleshooting

Test the boundary you changed, interpret safe diagnostics, and separate local fixtures from real database and platform evidence.

Start with the failing entry, exact app root, installed package artifacts and runtime. Record the reproducible command and expected behavior. Inspect only necessary environment-key presence; do not dump configuration or secrets to diagnose a missing setting.

These workflows refer to the audited TypeScript source at 549b987. Source checkout commands must be matched to an external app's real installed package exports and scripts.

Keep the feedback loop narrow

In the framework checkout, package exports resolve to dist. Build changed framework packages and dependencies before consumer tests/typechecks. Do not typecheck a consumer while another build clears that output. In an external application, use its installed artifacts and existing scripts; no framework checkout is required.

From the reviewed checkout root:

bun run cli help --jsonbun run cli inspect notes-operations create --root examples/notes --jsonbun run --filter @lenso/example-notes typecheckbun test examples/notes/test/operations.test.ts

The operation test uses isolated temporary SQLite/files and test-only identities. A read command still starts every installed plugin and may acquire resources. Only call a business operation after understanding the whole app's setup, trusted identity and target:

# Database, migration and a verified NOTES_SESSION are already configured.printf '%s\n' '{}' |  bun run cli call notes-operations list --root examples/notes --stdin --json

inspect imports trusted canonical config and converters but starts neither app nor Engine setup. It describes declarations, not database health or resolved config. check/generate/build run trusted Engine setup; generation/build write owned output. dev also runs the app. Those are different diagnostic effects.

Test ordinary services and ownership

Shared service tests should cover valid/invalid business input and the actual owner/tenant policy. An HTTP middleware test alone cannot establish that CLI/MCP/direct calls enforce the same rules. Keep fixed tokens and actors confined to fixtures; a production Auth source must verify real evidence.

Test resource failure paths as well as successful startup. This Bun unit test checks cleanup registered by a setup that later fails:

import { expect, test } from "bun:test";import { definePlugin, startApp } from "@lenso/core";
test("failing setup releases its acquired resource", async () => {  let released = false;  const resource = definePlugin({    id: "resource",    setup(context) {      context.onCleanup(() => { released = true; });      throw new Error("test setup failure");    },  });  await expect(startApp({ plugins: [resource] })).rejects.toThrow("test setup failure");  expect(released).toBe(true);});

The lifecycle tests additionally cover global LIFO, disposer sharing, repeated stop and aggregated failures. For a real resource, verify that owned clients close and borrowed clients survive. Streaming entries must test body completion/cancellation, not just response creation.

Use real integration evidence where it matters

Check from source rootWhat it establishes and prerequisites
bun test packages/db/test/resources.test.tsReal Bun SQLite ownership/isolation, no implicit migration
bun test examples/notes/test/notes.test.ts examples/notes/test/operations.test.tsShared Notes/CLI validation and owner boundaries with isolated local resources
bun test packages/mcp/test/stdio.test.tsReal subprocess and official SDK stdio behavior
bun test packages/manage/test/manage.test.ts examples/notes/test/manage.test.tsFinite output, exact instance selection, trusted context, permission/confirmation/approval and service ownership with controlled/local fixtures
bun test packages/storage/test/files.test.tsFile-state/compensation races using controlled providers; not live cloud behavior
bun test packages/tasks/test/worker.test.tsControlled worker drain/cancellation/slot semantics; not persistence
bun test packages/otel/test/cli.test.tsFinite CLI export behavior with test receivers/exporters

Database/platform checks need their explicit prerequisites:

# Dedicated disposable DB; environment already contains its URL.LENSO_TEST_DATABASE_URL="$DATABASE_URL" bun test examples/notes/test/postgres.test.tsTASK_TEST_DATABASE_URL="$DATABASE_URL" bun test packages/tasks/test/postgres.test.ts
# Existing local workerd/D1 setup, from the source root.bun run --cwd examples/workers test:notes

The Notes PostgreSQL test exercises real CLI/HTTP. The Tasks provider test creates uniquely named queues and exercises real processes/SIGKILL. The Tasks example integration test is separate: run its explicit migration on an isolated DB first, then TASK_TEST_DATABASE_URL="$DATABASE_URL" bun test src/postgres.test.ts src/entry.test.ts from examples/tasks. Those example tests do not migrate or delete shared data.

Auth's LENSO_REQUIRE_POSTGRES=1 bun test packages/auth/test/postgres.test.ts requires installed PostgreSQL binaries and starts private loopback clusters; the required flag fails instead of skipping if unavailable. Local workerd tests establish local D1 behavior, not production replication. Report missing prerequisites and skipped tests as gaps, never as passing persistence/deployment checks.

Read code, phase and ordered causes

Finite CLI commands put one schemaVersion: 1 JSON envelope on stdout and logs on stderr. Exit 2 identifies arguments/input, 3 identifies discovery/assembly, and 1 identifies runtime/build/output failure. dev --json is unsupported. Use the diagnostic's actual code, phase, source and ordered causes before deciding on a fix.

For example, Notes rejects a blank title before setup:

printf '%s\n' '{"title":" "}' |  bun run cli call notes-operations create --root examples/notes --stdin --json

Expected evidence is code: "invalid-input", phase: "input", exit 2 and a safe field path for title; no DB or session should be needed to reject it. The existing operation tests verify that validation happens before setup.

EvidenceLikely boundary and next check
missing-dependency, duplicate-id, cyclic-dependencyAssembly: installed IDs, exact references and source locations
unknown-operation, invalid-operationsExplicit declarations: exact plugin object, named export and shared schema
config-invalid, phase configAll-instance preflight: safe nested cause code, field path and source ID
UNAUTHORIZED / FORBIDDENTrusted evidence, realm/audience and actual object owner; never inject an actor into JSON
invocation-failedUnknown business failure: consult authorized safe logs; client exception text is intentionally opaque
missing-context-binding, confirmation-required, approval-requiredTrusted entry binding/gate: do not add actor, confirm or approve flags to business JSON
output-too-large, phase outputFinite-operation JSON budget (default 1 MiB); effects may already have committed
invocation-and-cleanup-failedBoth invocation and cleanup failed: preserve both ordered causes
serialization-failedOutput is not plain finite JSON, including undefined; business work may already have committed
MCP adapter-busyExisting call still owns admission; it is not queued or safe to duplicate

Configuration preflight may read sources but begins before business setup. Static inspect never invokes source.read. A missing file/source, invalid env conversion and trusted TS config import are different failures; see configuration.

Avoid replay after uncertainty

Cleanup is resource release, not compensation. An output error, cleanup error, lost response or telemetry timeout can follow a committed business write. Query the application's authorized state before retrying. Task cancellation and final status do not prove all external effects were rolled back; use task recovery rules.

Check generated-output failures at the source/owner boundary. .lenso and dist are reproducible output: edit source/config and regenerate, rather than patching generated files or deleting unrelated work. Missing/mismatched database schema calls for an explicit authorized migration workflow, not a startup workaround.

Finish with a useful report

Report the runtime/package baseline, reproduced command, diagnostic code/phase/source, focused changes and checks actually run. Separate local fixtures, real DB/workerd evidence and skips. Mention unresolved state after uncertain side effects and name a missing authorized status/log interface when it prevents diagnosis. Use observability for existing stderr/backend evidence and Manage for explicitly selected authorized operations. There is no generic Observe query command or automatic remote control service.