cf-bun-mocks 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +70 -17
- package/package.json +4 -4
- package/src/d1.ts +19 -11
- package/src/index.ts +1 -1
- package/src/workers.ts +35 -0
- package/src/env.ts +0 -17
package/README.md
CHANGED
|
@@ -26,12 +26,12 @@ const db = new D1Mock("./test.db");
|
|
|
26
26
|
|
|
27
27
|
### With Migrations
|
|
28
28
|
|
|
29
|
-
Use `
|
|
29
|
+
Use `createD1Mock` to create an in-memory database and run your migrations:
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
import {
|
|
32
|
+
import { createD1Mock } from "cf-bun-mocks";
|
|
33
33
|
|
|
34
|
-
const db = await
|
|
34
|
+
const db = await createD1Mock("./migrations");
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
This reads all `.sql` files from the migrations directory in sorted order and executes them.
|
|
@@ -107,30 +107,79 @@ const env: Env = {
|
|
|
107
107
|
const response = await worker.fetch(request, env);
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
## Environment
|
|
110
|
+
## Workers Environment
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
For testing Cloudflare Workers, use the `useWorkersEnv` helper to create test environments:
|
|
113
113
|
|
|
114
114
|
```typescript
|
|
115
115
|
import { describe, test, expect } from "bun:test";
|
|
116
|
-
import {
|
|
116
|
+
import { useWorkersEnv, D1Mock } from "cf-bun-mocks";
|
|
117
117
|
import type { Env } from "./worker";
|
|
118
118
|
|
|
119
119
|
describe("my worker", () => {
|
|
120
|
-
|
|
120
|
+
const { env } = useWorkersEnv<Env>(() => ({
|
|
121
121
|
DB: new D1Mock(":memory:"),
|
|
122
122
|
MY_SECRET: "test-secret",
|
|
123
123
|
}));
|
|
124
124
|
|
|
125
|
-
test("uses
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
expect(result).toBeDefined();
|
|
125
|
+
test("uses test environment", async () => {
|
|
126
|
+
// Pass the env to your worker handler
|
|
127
|
+
const response = await worker.fetch(request, env);
|
|
128
|
+
expect(response.status).toBe(200);
|
|
130
129
|
});
|
|
131
130
|
});
|
|
132
131
|
```
|
|
133
132
|
|
|
133
|
+
### Module Mocking (Advanced)
|
|
134
|
+
|
|
135
|
+
For mocking `cloudflare:workers` module imports, use `setupWorkersMock` in a preload script and combine it with `useWorkersEnv`:
|
|
136
|
+
|
|
137
|
+
Create a `test-setup.ts` file:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// test-setup.ts
|
|
141
|
+
import { setupWorkersMock } from "cf-bun-mocks";
|
|
142
|
+
|
|
143
|
+
// Set up module mocks before any test files load
|
|
144
|
+
await setupWorkersMock();
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Then run tests with preload:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
bun test --preload ./test-setup.ts
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Or configure it in `bunfig.toml`:
|
|
154
|
+
|
|
155
|
+
```toml
|
|
156
|
+
[test]
|
|
157
|
+
preload = ["./test-setup.ts"]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then use in your tests:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { describe, test, expect } from "bun:test";
|
|
164
|
+
import { useWorkersEnv, D1Mock } from "cf-bun-mocks";
|
|
165
|
+
|
|
166
|
+
describe("worker with module imports", () => {
|
|
167
|
+
useWorkersEnv(() => ({
|
|
168
|
+
DB: new D1Mock(":memory:"),
|
|
169
|
+
API_KEY: "test-key",
|
|
170
|
+
}));
|
|
171
|
+
|
|
172
|
+
test("uses mocked module", async () => {
|
|
173
|
+
// The env object reference stays the same, only properties change
|
|
174
|
+
const { env } = await import("cloudflare:workers");
|
|
175
|
+
expect(env.DB).toBeDefined();
|
|
176
|
+
expect(env.API_KEY).toBe("test-key");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
> **Note**: `setupWorkersMock` uses Bun's `mock.module()` function. Module mocks must be set up before any imports happen. See [Bun's test lifecycle docs](https://bun.com/docs/test/lifecycle#global-setup-and-teardown) for preload details and [Bun's mocking docs](https://bun.sh/docs/test/mocking) for more on module mocking.
|
|
182
|
+
|
|
134
183
|
## API
|
|
135
184
|
|
|
136
185
|
### `D1Mock`
|
|
@@ -139,17 +188,21 @@ Implements the full `D1Database` interface from `@cloudflare/workers-types`:
|
|
|
139
188
|
|
|
140
189
|
- `prepare(query: string)` - Create a prepared statement
|
|
141
190
|
- `batch(statements: D1PreparedStatement[])` - Execute multiple statements
|
|
142
|
-
- `exec(query: string)` - Execute raw SQL
|
|
191
|
+
- `exec(query: string)` - Execute raw SQL (supports multiple statements)
|
|
143
192
|
- `dump()` - Serialize the database to an ArrayBuffer
|
|
144
193
|
- `withSession(constraint?)` - Get a session (bookmark tracking is stubbed)
|
|
145
194
|
|
|
146
|
-
### `
|
|
195
|
+
### `createD1Mock(migrationsPath: string)`
|
|
196
|
+
|
|
197
|
+
Creates an in-memory D1Mock and runs all `.sql` migration files from the specified directory in sorted order.
|
|
198
|
+
|
|
199
|
+
### `useWorkersEnv<TEnv>(createEnv: () => Partial<TEnv> | Promise<Partial<TEnv>>)`
|
|
147
200
|
|
|
148
|
-
|
|
201
|
+
Updates the global workers mock environment for each test. Must be used with `setupWorkersMock()`.
|
|
149
202
|
|
|
150
|
-
### `
|
|
203
|
+
### `setupWorkersMock<TEnv>(createMock?: () => WorkersModuleMock<TEnv> | Promise<WorkersModuleMock<TEnv>>)`
|
|
151
204
|
|
|
152
|
-
|
|
205
|
+
Sets up the `cloudflare:workers` module mock using Bun's `mock.module()`. **Must be called in a preload script before any test files load.** Use Bun's `--preload` flag or configure it in `bunfig.toml`. Call once at the start of your test suite, then use `useWorkersEnv()` to update the environment per test.
|
|
153
206
|
|
|
154
207
|
## Requirements
|
|
155
208
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-bun-mocks",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Cloudflare Workers mocks and helpers for Bun testing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"import": "./src/d1.ts",
|
|
16
16
|
"types": "./src/d1.ts"
|
|
17
17
|
},
|
|
18
|
-
"./
|
|
19
|
-
"import": "./src/
|
|
20
|
-
"types": "./src/
|
|
18
|
+
"./workers": {
|
|
19
|
+
"import": "./src/workers.ts",
|
|
20
|
+
"types": "./src/workers.ts"
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
package/src/d1.ts
CHANGED
|
@@ -110,7 +110,7 @@ class D1DatabaseSessionMock implements D1DatabaseSession {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
export class D1Mock implements D1Database {
|
|
113
|
-
|
|
113
|
+
db: Database;
|
|
114
114
|
|
|
115
115
|
constructor(
|
|
116
116
|
filename?: string,
|
|
@@ -124,11 +124,11 @@ export class D1Mock implements D1Database {
|
|
|
124
124
|
strict?: boolean;
|
|
125
125
|
}
|
|
126
126
|
) {
|
|
127
|
-
this
|
|
127
|
+
this.db = new Database(filename, options);
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
prepare(query: string): D1PreparedStatement {
|
|
131
|
-
const stmt = this
|
|
131
|
+
const stmt = this.db.prepare(query);
|
|
132
132
|
return new D1PreparedStatementMock(stmt);
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -147,12 +147,20 @@ export class D1Mock implements D1Database {
|
|
|
147
147
|
|
|
148
148
|
async exec(query: string): Promise<D1ExecResult> {
|
|
149
149
|
const start = performance.now();
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
try {
|
|
151
|
+
const { changes: count } = this.db.run(query);
|
|
152
|
+
const duration = performance.now() - start;
|
|
153
|
+
return {
|
|
154
|
+
count,
|
|
155
|
+
duration,
|
|
156
|
+
};
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`D1Mock exec failed: ${
|
|
160
|
+
error instanceof Error ? error.message : String(error)
|
|
161
|
+
}\nQuery: ${query}`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
withSession(
|
|
@@ -162,7 +170,7 @@ export class D1Mock implements D1Database {
|
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
async dump(): Promise<ArrayBuffer> {
|
|
165
|
-
const serialized = this
|
|
173
|
+
const serialized = this.db.serialize();
|
|
166
174
|
const buffer = serialized.buffer;
|
|
167
175
|
if (buffer instanceof SharedArrayBuffer) {
|
|
168
176
|
const newBuffer = new ArrayBuffer(buffer.byteLength);
|
|
@@ -173,7 +181,7 @@ export class D1Mock implements D1Database {
|
|
|
173
181
|
}
|
|
174
182
|
}
|
|
175
183
|
|
|
176
|
-
export async function
|
|
184
|
+
export async function createD1Mock(migrationsPath: string): Promise<D1Mock> {
|
|
177
185
|
const files = readdirSync(migrationsPath)
|
|
178
186
|
.filter((file) => file.endsWith(".sql"))
|
|
179
187
|
.sort();
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from "./d1.ts";
|
|
2
|
-
export * from "./
|
|
2
|
+
export * from "./workers.ts";
|
package/src/workers.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
+
import { beforeEach, mock, afterAll } from "bun:test";
|
|
3
|
+
|
|
4
|
+
type WorkersModuleMock<TEnv extends Cloudflare.Env = Cloudflare.Env> = {
|
|
5
|
+
env: Partial<TEnv>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
let moduleMock: WorkersModuleMock = { env: {} };
|
|
9
|
+
|
|
10
|
+
export async function setupWorkersMock<
|
|
11
|
+
TEnv extends Cloudflare.Env = Cloudflare.Env
|
|
12
|
+
>(createMock?: () => Bun.MaybePromise<WorkersModuleMock<TEnv>>) {
|
|
13
|
+
if (createMock) {
|
|
14
|
+
moduleMock = await createMock();
|
|
15
|
+
}
|
|
16
|
+
mock.module("cloudflare:workers", () => moduleMock);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useWorkersEnv<TEnv extends Cloudflare.Env = Cloudflare.Env>(
|
|
20
|
+
createEnv: () => Bun.MaybePromise<Partial<TEnv>>
|
|
21
|
+
) {
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
const env = await createEnv();
|
|
24
|
+
for (const key in moduleMock.env) {
|
|
25
|
+
delete (moduleMock.env as any)[key];
|
|
26
|
+
}
|
|
27
|
+
Object.assign(moduleMock.env, env);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterAll(() => {
|
|
31
|
+
moduleMock.env = {};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return moduleMock.env;
|
|
35
|
+
}
|
package/src/env.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/// <reference types="@cloudflare/workers-types" />
|
|
2
|
-
import { beforeEach, afterEach, mock } from "bun:test";
|
|
3
|
-
|
|
4
|
-
const MODULE_NAME = "cloudflare:workers";
|
|
5
|
-
|
|
6
|
-
export function useEnv<TEnv extends Cloudflare.Env = Cloudflare.Env>(
|
|
7
|
-
setup: () => Bun.MaybePromise<Partial<TEnv>>
|
|
8
|
-
) {
|
|
9
|
-
beforeEach(async () => {
|
|
10
|
-
const env = await setup();
|
|
11
|
-
mock.module(MODULE_NAME, () => ({ env }));
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterEach(() => {
|
|
15
|
-
mock.module(MODULE_NAME, () => ({ env: {} }));
|
|
16
|
-
});
|
|
17
|
-
}
|