Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Comprehensive Cloudflare platform skill covering Workers, D1, R2, KV, AI, Durable Objects, and security.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/do-storage/testing.md
1# DO Storage Testing23Testing Durable Objects with storage using `vitest-pool-workers`.45## Setup67**vitest.config.ts:**8```typescript9import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";1011export default defineWorkersConfig({12test: {13poolOptions: {14workers: { wrangler: { configPath: "./wrangler.toml" } }15}16}17});18```1920**package.json:** Add `@cloudflare/vitest-pool-workers` and `vitest` to devDependencies2122## Basic Testing2324```typescript25import { env, runInDurableObject } from "cloudflare:test";26import { describe, it, expect } from "vitest";2728describe("Counter DO", () => {29it("increments counter", async () => {30const id = env.COUNTER.idFromName("test");31const result = await runInDurableObject(env.COUNTER, id, async (instance, state) => {32const val1 = await instance.increment();33const val2 = await instance.increment();34return { val1, val2 };35});36expect(result.val1).toBe(1);37expect(result.val2).toBe(2);38});39});40```4142## Testing SQL Storage4344```typescript45it("creates and queries users", async () => {46const id = env.USER_MANAGER.idFromName("test");47await runInDurableObject(env.USER_MANAGER, id, async (instance, state) => {48await instance.createUser("[email protected]", "Alice");49const user = await instance.getUser("[email protected]");50expect(user).toEqual({ email: "[email protected]", name: "Alice" });51});52});5354it("handles schema migrations", async () => {55const id = env.USER_MANAGER.idFromName("migration-test");56await runInDurableObject(env.USER_MANAGER, id, async (instance, state) => {57const version = state.storage.sql.exec(58"SELECT value FROM _meta WHERE key = 'schema_version'"59).one()?.value;60expect(version).toBe("1");61});62});63```6465## Testing Alarms6667```typescript68import { runDurableObjectAlarm } from "cloudflare:test";6970it("processes batch on alarm", async () => {71const id = env.BATCH_PROCESSOR.idFromName("test");7273// Add items74await runInDurableObject(env.BATCH_PROCESSOR, id, async (instance) => {75await instance.addItem("item1");76await instance.addItem("item2");77});7879// Trigger alarm80await runDurableObjectAlarm(env.BATCH_PROCESSOR, id);8182// Verify processed83await runInDurableObject(env.BATCH_PROCESSOR, id, async (instance, state) => {84const count = state.storage.sql.exec(85"SELECT COUNT(*) as count FROM processed_items"86).one().count;87expect(count).toBe(2);88});89});90```9192## Testing Concurrency9394```typescript95it("handles concurrent increments safely", async () => {96const id = env.COUNTER.idFromName("concurrent-test");9798// Parallel increments99const results = await Promise.all([100runInDurableObject(env.COUNTER, id, (i) => i.increment()),101runInDurableObject(env.COUNTER, id, (i) => i.increment()),102runInDurableObject(env.COUNTER, id, (i) => i.increment())103]);104105// All should get unique values106expect(new Set(results).size).toBe(3);107expect(Math.max(...results)).toBe(3);108});109```110111## Test Isolation112113```typescript114// Per-test unique IDs115let testId: string;116beforeEach(() => { testId = crypto.randomUUID(); });117118it("isolated test", async () => {119const id = env.MY_DO.idFromName(testId);120// Uses unique DO instance121});122123// Cleanup pattern124it("with cleanup", async () => {125const id = env.MY_DO.idFromName("cleanup-test");126try {127await runInDurableObject(env.MY_DO, id, async (instance) => {});128} finally {129await runInDurableObject(env.MY_DO, id, async (instance, state) => {130await state.storage.deleteAll();131});132}133});134```135136## Testing PITR137138```typescript139it("restores from bookmark", async () => {140const id = env.MY_DO.idFromName("pitr-test");141142// Create checkpoint143const bookmark = await runInDurableObject(env.MY_DO, id, async (instance, state) => {144await state.storage.put("value", 1);145return await state.storage.getCurrentBookmark();146});147148// Modify and restore149await runInDurableObject(env.MY_DO, id, async (instance, state) => {150await state.storage.put("value", 2);151await state.storage.onNextSessionRestoreBookmark(bookmark);152state.abort();153});154155// Verify restored156await runInDurableObject(env.MY_DO, id, async (instance, state) => {157const value = await state.storage.get("value");158expect(value).toBe(1);159});160});161```162163## Testing Transactions164165```typescript166it("rolls back on error", async () => {167const id = env.BANK.idFromName("transaction-test");168169await runInDurableObject(env.BANK, id, async (instance, state) => {170await state.storage.put("balance", 100);171172await expect(173state.storage.transaction(async () => {174await state.storage.put("balance", 50);175throw new Error("Cancel");176})177).rejects.toThrow("Cancel");178179const balance = await state.storage.get("balance");180expect(balance).toBe(100); // Rolled back181});182});183```184