Files
dotfiles/dot_agents/skills/cloudflare/references/miniflare/patterns.md
T
Prad Nukala 6406208b35 shell: fish functions -> ~/.local/bin executables + bash/zsh ports; manage ~/.agents
- 16 cross-shell utilities converted to sh/bash chezmoi executables:
  ai c2p cp-appid extract_agents fab gdc gh-sonr-org-migrate ghg-clone
  ghn gho git-summary launch_omp_livediff_overlay lc-elir mdcp mdp pj
  (ghg-clone not named ghg: unrelated cargo binary owns that name)
- cwd/history helpers stay per-shell: yy c cd clip-last + ghg/ghn/gho
  cd-wrappers in bash/functions/ and zsh/functions/, loaded by rc files
- fish keeps only shell-state functions; converted ones now hit PATH
- cp-appid ignore gating moved to .local/bin path
- dot_agents/skills: 82 skills materialized from dotstate storage so
  dot_claude skill symlinks resolve on fresh bootstrap; dotstate
  profile/symlinks.json released .agents/skills .bashrc .zshrc
2026-08-10 15:15:06 -04:00

182 lines
4.2 KiB
Markdown

# Testing Patterns
## Choosing a Testing Approach
| Approach | Use Case | Speed | Setup | Runtime |
|----------|----------|-------|-------|---------|
| **getPlatformProxy** | Unit tests, logic testing | Fast | Low | Miniflare |
| **Miniflare API** | Integration tests, full control | Medium | Medium | Miniflare |
| **vitest-pool-workers** | Vitest runner integration | Medium | Medium | workerd |
**Quick guide:**
- Unit tests → getPlatformProxy
- Integration tests → Miniflare API
- Vitest workflows → vitest-pool-workers
## getPlatformProxy
Lightweight unit testing - provides bindings without full Worker runtime.
```js
// vitest.config.js
export default { test: { environment: "node" } };
```
```js
import { env } from "cloudflare:test";
import { describe, it, expect } from "vitest";
describe("Business logic", () => {
it("processes data with KV", async () => {
await env.KV.put("test", "value");
expect(await env.KV.get("test")).toBe("value");
});
});
```
**Pros:** Fast, simple
**Cons:** No full runtime, can't test fetch handler
## vitest-pool-workers
Full Workers runtime in Vitest. Reads `wrangler.toml`.
```bash
npm i -D @cloudflare/vitest-pool-workers
```
```js
// vitest.config.js
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersConfig({
test: {
poolOptions: { workers: { wrangler: { configPath: "./wrangler.toml" } } },
},
});
```
```js
import { env, SELF } from "cloudflare:test";
import { it, expect } from "vitest";
it("handles fetch", async () => {
const res = await SELF.fetch("http://example.com/");
expect(res.status).toBe(200);
});
```
**Pros:** Full runtime, uses wrangler.toml
**Cons:** Requires Wrangler config
## Miniflare API (node:test)
```js
import assert from "node:assert";
import test, { after, before } from "node:test";
import { Miniflare } from "miniflare";
let mf;
before(() => {
mf = new Miniflare({ scriptPath: "src/index.js", kvNamespaces: ["TEST_KV"] });
});
test("fetch", async () => {
const res = await mf.dispatchFetch("http://localhost/");
assert.strictEqual(await res.text(), "Hello");
});
after(() => mf.dispose());
```
## Testing Durable Objects & Events
```js
// Durable Objects
const ns = await mf.getDurableObjectNamespace("COUNTER");
const stub = ns.get(ns.idFromName("test-counter"));
await stub.fetch("http://localhost/increment");
// Direct storage
const storage = await mf.getDurableObjectStorage(ns.idFromName("test-counter"));
const count = await storage.get("count");
// Queue
const worker = await mf.getWorker();
await worker.queue("my-queue", [
{ id: "msg1", timestamp: new Date(), body: { userId: 123 }, attempts: 1 },
]);
// Scheduled
await worker.scheduled({ cron: "0 0 * * *" });
```
## Test Isolation & Mocking
```js
// Per-test isolation
beforeEach(() => { mf = new Miniflare({ kvNamespaces: ["TEST"] }); });
afterEach(() => mf.dispose());
// Mock external APIs
new Miniflare({
workers: [
{ name: "main", serviceBindings: { API: "mock-api" }, script: `...` },
{ name: "mock-api", script: `export default { async fetch() { return Response.json({mock: true}); } }` },
],
});
```
## Type Safety
```ts
import type { KVNamespace } from "@cloudflare/workers-types";
interface Env {
KV: KVNamespace;
API_KEY: string;
}
const env = await mf.getBindings<Env>();
await env.KV.put("key", "value"); // Typed!
export default {
async fetch(req: Request, env: Env) {
return new Response(await env.KV.get("key"));
}
} satisfies ExportedHandler<Env>;
```
## WebSocket Testing
```js
const res = await mf.dispatchFetch("http://localhost/ws", {
headers: { Upgrade: "websocket" },
});
assert.strictEqual(res.status, 101);
```
## Migration from unstable_dev
```js
// Old (deprecated)
import { unstable_dev } from "wrangler";
const worker = await unstable_dev("src/index.ts");
// New
import { Miniflare } from "miniflare";
const mf = new Miniflare({ scriptPath: "src/index.ts" });
```
## CI/CD Tips
```js
// In-memory storage (faster)
new Miniflare({ kvNamespaces: ["TEST"] }); // No persist = in-memory
// Use dispatchFetch (no port conflicts)
await mf.dispatchFetch("http://localhost/");
```
See [gotchas.md](./gotchas.md) for troubleshooting.