mirror of
https://github.com/prdlk/dotfiles.git
synced 2026-11-04 03:47:36 +00:00
config(git,claude,ssh): templated gitconfig; claude settings/commands/hooks/skills; ssh config
gitconfig: name/email from chezmoi data, gh credential helper by PATH lookup (portable to darwin). claude: no credentials, history, projects, sessions, or plugin caches. ssh config as private_ (0600). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3c691e8dc5
commit
20891f3910
476 files changed
+59086
-7
No files matched your search
@@ -0,0 +1,264 @@
|
||||
# Testing Durable Objects
|
||||
|
||||
Use `@cloudflare/vitest-pool-workers` to test DOs inside the Workers runtime.
|
||||
|
||||
## Setup
|
||||
|
||||
### Install Dependencies
|
||||
|
||||
```bash
|
||||
npm i -D vitest@~3.2.0 @cloudflare/vitest-pool-workers
|
||||
```
|
||||
|
||||
### vitest.config.ts
|
||||
|
||||
```typescript
|
||||
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
||||
|
||||
export default defineWorkersConfig({
|
||||
test: {
|
||||
poolOptions: {
|
||||
workers: {
|
||||
wrangler: { configPath: "./wrangler.toml" },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### TypeScript Config (test/tsconfig.json)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "bundler",
|
||||
"types": ["@cloudflare/vitest-pool-workers"]
|
||||
},
|
||||
"include": ["./**/*.ts", "../src/worker-configuration.d.ts"]
|
||||
}
|
||||
```
|
||||
|
||||
### Environment Types (env.d.ts)
|
||||
|
||||
```typescript
|
||||
declare module "cloudflare:test" {
|
||||
interface ProvidedEnv extends Env {}
|
||||
}
|
||||
```
|
||||
|
||||
## Unit Tests (Direct DO Access)
|
||||
|
||||
```typescript
|
||||
import { env } from "cloudflare:test";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("Counter DO", () => {
|
||||
it("should increment", async () => {
|
||||
const stub = env.COUNTER.getByName("test-counter");
|
||||
|
||||
expect(await stub.increment()).toBe(1);
|
||||
expect(await stub.increment()).toBe(2);
|
||||
expect(await stub.getCount()).toBe(2);
|
||||
});
|
||||
|
||||
it("isolates different instances", async () => {
|
||||
const stub1 = env.COUNTER.getByName("counter-1");
|
||||
const stub2 = env.COUNTER.getByName("counter-2");
|
||||
|
||||
await stub1.increment();
|
||||
await stub1.increment();
|
||||
await stub2.increment();
|
||||
|
||||
expect(await stub1.getCount()).toBe(2);
|
||||
expect(await stub2.getCount()).toBe(1);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Integration Tests (HTTP via SELF)
|
||||
|
||||
```typescript
|
||||
import { SELF } from "cloudflare:test";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("Worker HTTP", () => {
|
||||
it("should increment via POST", async () => {
|
||||
const res = await SELF.fetch("http://example.com?id=test", {
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
expect(res.status).toBe(200);
|
||||
const data = await res.json<{ count: number }>();
|
||||
expect(data.count).toBe(1);
|
||||
});
|
||||
|
||||
it("should get count via GET", async () => {
|
||||
await SELF.fetch("http://example.com?id=get-test", { method: "POST" });
|
||||
await SELF.fetch("http://example.com?id=get-test", { method: "POST" });
|
||||
|
||||
const res = await SELF.fetch("http://example.com?id=get-test");
|
||||
const data = await res.json<{ count: number }>();
|
||||
expect(data.count).toBe(2);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Direct Internal Access
|
||||
|
||||
Use `runInDurableObject()` to access instance internals and storage:
|
||||
|
||||
```typescript
|
||||
import { env, runInDurableObject } from "cloudflare:test";
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { Counter } from "../src";
|
||||
|
||||
describe("DO internals", () => {
|
||||
it("can verify storage directly", async () => {
|
||||
const stub = env.COUNTER.getByName("direct-test");
|
||||
await stub.increment();
|
||||
await stub.increment();
|
||||
|
||||
await runInDurableObject(stub, async (instance: Counter, state) => {
|
||||
expect(instance).toBeInstanceOf(Counter);
|
||||
|
||||
const result = state.storage.sql
|
||||
.exec<{ value: number }>(
|
||||
"SELECT value FROM counters WHERE name = ?",
|
||||
"default"
|
||||
)
|
||||
.one();
|
||||
expect(result.value).toBe(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## List DO IDs
|
||||
|
||||
```typescript
|
||||
import { env, listDurableObjectIds } from "cloudflare:test";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("DO listing", () => {
|
||||
it("can list all IDs in namespace", async () => {
|
||||
const id1 = env.COUNTER.idFromName("list-1");
|
||||
const id2 = env.COUNTER.idFromName("list-2");
|
||||
|
||||
await env.COUNTER.get(id1).increment();
|
||||
await env.COUNTER.get(id2).increment();
|
||||
|
||||
const ids = await listDurableObjectIds(env.COUNTER);
|
||||
expect(ids.length).toBe(2);
|
||||
expect(ids.some(id => id.equals(id1))).toBe(true);
|
||||
expect(ids.some(id => id.equals(id2))).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Testing Alarms
|
||||
|
||||
Use `runDurableObjectAlarm()` to trigger alarms immediately:
|
||||
|
||||
```typescript
|
||||
import { env, runInDurableObject, runDurableObjectAlarm } from "cloudflare:test";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
describe("DO alarms", () => {
|
||||
it("can trigger alarms immediately", async () => {
|
||||
const stub = env.COUNTER.getByName("alarm-test");
|
||||
await stub.increment();
|
||||
await stub.increment();
|
||||
expect(await stub.getCount()).toBe(2);
|
||||
|
||||
// Schedule alarm
|
||||
await runInDurableObject(stub, async (instance, state) => {
|
||||
await state.storage.setAlarm(Date.now() + 60_000);
|
||||
});
|
||||
|
||||
// Execute immediately without waiting
|
||||
const ran = await runDurableObjectAlarm(stub);
|
||||
expect(ran).toBe(true);
|
||||
|
||||
// Verify alarm handler ran (if it resets counter)
|
||||
expect(await stub.getCount()).toBe(0);
|
||||
|
||||
// No alarm scheduled now
|
||||
const ranAgain = await runDurableObjectAlarm(stub);
|
||||
expect(ranAgain).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Example alarm handler:
|
||||
```typescript
|
||||
async alarm(): Promise<void> {
|
||||
this.ctx.storage.sql.exec("DELETE FROM counters");
|
||||
}
|
||||
```
|
||||
|
||||
## Test Isolation
|
||||
|
||||
Each test gets isolated storage automatically. DOs from one test don't affect others:
|
||||
|
||||
```typescript
|
||||
describe("Isolation", () => {
|
||||
it("first test creates DO", async () => {
|
||||
const stub = env.COUNTER.getByName("isolated");
|
||||
await stub.increment();
|
||||
expect(await stub.getCount()).toBe(1);
|
||||
});
|
||||
|
||||
it("second test has fresh state", async () => {
|
||||
const ids = await listDurableObjectIds(env.COUNTER);
|
||||
expect(ids.length).toBe(0); // Previous test's DO is gone
|
||||
|
||||
const stub = env.COUNTER.getByName("isolated");
|
||||
expect(await stub.getCount()).toBe(0); // Fresh instance
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## SQLite Storage Testing
|
||||
|
||||
```typescript
|
||||
describe("SQLite", () => {
|
||||
it("can verify SQL storage", async () => {
|
||||
const stub = env.COUNTER.getByName("sqlite-test");
|
||||
await stub.increment("page-views");
|
||||
await stub.increment("page-views");
|
||||
await stub.increment("api-calls");
|
||||
|
||||
await runInDurableObject(stub, async (instance, state) => {
|
||||
const rows = state.storage.sql
|
||||
.exec<{ name: string; value: number }>(
|
||||
"SELECT name, value FROM counters ORDER BY name"
|
||||
)
|
||||
.toArray();
|
||||
|
||||
expect(rows).toEqual([
|
||||
{ name: "api-calls", value: 1 },
|
||||
{ name: "page-views", value: 2 },
|
||||
]);
|
||||
|
||||
expect(state.storage.sql.databaseSize).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
npx vitest # Watch mode
|
||||
npx vitest run # Single run
|
||||
```
|
||||
|
||||
package.json:
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"test": "vitest"
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in new issue
Block a user