@xivdyetools/test-utils 1.0.0
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/LICENSE +37 -0
- package/README.md +144 -0
- package/dist/assertions/index.d.ts +7 -0
- package/dist/assertions/index.d.ts.map +1 -0
- package/dist/assertions/index.js +7 -0
- package/dist/assertions/index.js.map +1 -0
- package/dist/assertions/response.d.ts +84 -0
- package/dist/assertions/response.d.ts.map +1 -0
- package/dist/assertions/response.js +125 -0
- package/dist/assertions/response.js.map +1 -0
- package/dist/auth/context.d.ts +55 -0
- package/dist/auth/context.d.ts.map +1 -0
- package/dist/auth/context.js +95 -0
- package/dist/auth/context.js.map +1 -0
- package/dist/auth/headers.d.ts +64 -0
- package/dist/auth/headers.d.ts.map +1 -0
- package/dist/auth/headers.js +101 -0
- package/dist/auth/headers.js.map +1 -0
- package/dist/auth/index.d.ts +10 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/jwt.d.ts +76 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +77 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/signature.d.ts +64 -0
- package/dist/auth/signature.d.ts.map +1 -0
- package/dist/auth/signature.js +75 -0
- package/dist/auth/signature.js.map +1 -0
- package/dist/cloudflare/analytics.d.ts +55 -0
- package/dist/cloudflare/analytics.d.ts.map +1 -0
- package/dist/cloudflare/analytics.js +44 -0
- package/dist/cloudflare/analytics.js.map +1 -0
- package/dist/cloudflare/d1.d.ts +101 -0
- package/dist/cloudflare/d1.d.ts.map +1 -0
- package/dist/cloudflare/d1.js +148 -0
- package/dist/cloudflare/d1.js.map +1 -0
- package/dist/cloudflare/fetcher.d.ts +72 -0
- package/dist/cloudflare/fetcher.d.ts.map +1 -0
- package/dist/cloudflare/fetcher.js +136 -0
- package/dist/cloudflare/fetcher.js.map +1 -0
- package/dist/cloudflare/index.d.ts +12 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +12 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/cloudflare/kv.d.ts +85 -0
- package/dist/cloudflare/kv.d.ts.map +1 -0
- package/dist/cloudflare/kv.js +138 -0
- package/dist/cloudflare/kv.js.map +1 -0
- package/dist/cloudflare/r2.d.ts +88 -0
- package/dist/cloudflare/r2.d.ts.map +1 -0
- package/dist/cloudflare/r2.js +127 -0
- package/dist/cloudflare/r2.js.map +1 -0
- package/dist/constants/index.d.ts +8 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +8 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/constants/pkce.d.ts +89 -0
- package/dist/constants/pkce.d.ts.map +1 -0
- package/dist/constants/pkce.js +107 -0
- package/dist/constants/pkce.js.map +1 -0
- package/dist/constants/secrets.d.ts +72 -0
- package/dist/constants/secrets.d.ts.map +1 -0
- package/dist/constants/secrets.js +73 -0
- package/dist/constants/secrets.js.map +1 -0
- package/dist/dom/canvas.d.ts +108 -0
- package/dist/dom/canvas.d.ts.map +1 -0
- package/dist/dom/canvas.js +125 -0
- package/dist/dom/canvas.js.map +1 -0
- package/dist/dom/fetch.d.ts +69 -0
- package/dist/dom/fetch.d.ts.map +1 -0
- package/dist/dom/fetch.js +107 -0
- package/dist/dom/fetch.js.map +1 -0
- package/dist/dom/index.d.ts +12 -0
- package/dist/dom/index.d.ts.map +1 -0
- package/dist/dom/index.js +12 -0
- package/dist/dom/index.js.map +1 -0
- package/dist/dom/localStorage.d.ts +80 -0
- package/dist/dom/localStorage.d.ts.map +1 -0
- package/dist/dom/localStorage.js +124 -0
- package/dist/dom/localStorage.js.map +1 -0
- package/dist/dom/matchMedia.d.ts +51 -0
- package/dist/dom/matchMedia.d.ts.map +1 -0
- package/dist/dom/matchMedia.js +120 -0
- package/dist/dom/matchMedia.js.map +1 -0
- package/dist/dom/resizeObserver.d.ts +68 -0
- package/dist/dom/resizeObserver.d.ts.map +1 -0
- package/dist/dom/resizeObserver.js +99 -0
- package/dist/dom/resizeObserver.js.map +1 -0
- package/dist/factories/category.d.ts +74 -0
- package/dist/factories/category.d.ts.map +1 -0
- package/dist/factories/category.js +99 -0
- package/dist/factories/category.js.map +1 -0
- package/dist/factories/dye.d.ts +76 -0
- package/dist/factories/dye.d.ts.map +1 -0
- package/dist/factories/dye.js +211 -0
- package/dist/factories/dye.js.map +1 -0
- package/dist/factories/index.d.ts +13 -0
- package/dist/factories/index.d.ts.map +1 -0
- package/dist/factories/index.js +14 -0
- package/dist/factories/index.js.map +1 -0
- package/dist/factories/preset.d.ts +105 -0
- package/dist/factories/preset.d.ts.map +1 -0
- package/dist/factories/preset.js +170 -0
- package/dist/factories/preset.js.map +1 -0
- package/dist/factories/user.d.ts +74 -0
- package/dist/factories/user.d.ts.map +1 -0
- package/dist/factories/user.js +115 -0
- package/dist/factories/user.js.map +1 -0
- package/dist/factories/vote.d.ts +55 -0
- package/dist/factories/vote.d.ts.map +1 -0
- package/dist/factories/vote.js +68 -0
- package/dist/factories/vote.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/counters.d.ts +35 -0
- package/dist/utils/counters.d.ts.map +1 -0
- package/dist/utils/counters.js +53 -0
- package/dist/utils/counters.js.map +1 -0
- package/dist/utils/crypto.d.ts +42 -0
- package/dist/utils/crypto.d.ts.map +1 -0
- package/dist/utils/crypto.js +72 -0
- package/dist/utils/crypto.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock D1 Database for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of D1Database that tracks queries
|
|
5
|
+
* and allows custom mock responses for testing.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const db = createMockD1Database();
|
|
10
|
+
*
|
|
11
|
+
* // Setup mock responses
|
|
12
|
+
* db._setupMock((query, bindings) => {
|
|
13
|
+
* if (query.includes('SELECT') && query.includes('presets')) {
|
|
14
|
+
* return [{ id: 'preset-1', name: 'Test Preset' }];
|
|
15
|
+
* }
|
|
16
|
+
* return null;
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Use in tests
|
|
20
|
+
* const env = { DB: db as unknown as D1Database };
|
|
21
|
+
*
|
|
22
|
+
* // Assert queries were made
|
|
23
|
+
* expect(db._queries).toContain('SELECT * FROM presets');
|
|
24
|
+
* expect(db._bindings[0]).toEqual(['param1']);
|
|
25
|
+
*
|
|
26
|
+
* // Reset between tests
|
|
27
|
+
* db._reset();
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Creates a mock D1 database for testing
|
|
32
|
+
*
|
|
33
|
+
* The mock tracks all queries and bindings for assertions, and supports
|
|
34
|
+
* custom response functions for simulating database behavior.
|
|
35
|
+
*
|
|
36
|
+
* @returns A mock D1 database that can be cast to D1Database
|
|
37
|
+
*/
|
|
38
|
+
export function createMockD1Database() {
|
|
39
|
+
const queries = [];
|
|
40
|
+
const bindings = [];
|
|
41
|
+
let mockFn;
|
|
42
|
+
const createDefaultMeta = () => ({
|
|
43
|
+
duration: 0,
|
|
44
|
+
changes: 0,
|
|
45
|
+
last_row_id: 0,
|
|
46
|
+
rows_read: 0,
|
|
47
|
+
rows_written: 0,
|
|
48
|
+
});
|
|
49
|
+
const createStatement = (query) => {
|
|
50
|
+
let boundValues = [];
|
|
51
|
+
const statement = {
|
|
52
|
+
bind: (...values) => {
|
|
53
|
+
boundValues = values;
|
|
54
|
+
bindings.push(values);
|
|
55
|
+
return statement;
|
|
56
|
+
},
|
|
57
|
+
first: async () => {
|
|
58
|
+
queries.push(query);
|
|
59
|
+
if (mockFn) {
|
|
60
|
+
const result = mockFn(query, boundValues);
|
|
61
|
+
// If result is an array, return first element
|
|
62
|
+
if (Array.isArray(result)) {
|
|
63
|
+
return (result[0] ?? null);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
},
|
|
69
|
+
all: async () => {
|
|
70
|
+
queries.push(query);
|
|
71
|
+
if (mockFn) {
|
|
72
|
+
const result = mockFn(query, boundValues);
|
|
73
|
+
if (Array.isArray(result)) {
|
|
74
|
+
return { results: result, success: true, meta: createDefaultMeta() };
|
|
75
|
+
}
|
|
76
|
+
// If single object returned, wrap in array
|
|
77
|
+
if (result && typeof result === 'object') {
|
|
78
|
+
return { results: [result], success: true, meta: createDefaultMeta() };
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { results: [], success: true, meta: createDefaultMeta() };
|
|
82
|
+
},
|
|
83
|
+
run: async () => {
|
|
84
|
+
queries.push(query);
|
|
85
|
+
if (mockFn) {
|
|
86
|
+
const result = mockFn(query, boundValues);
|
|
87
|
+
if (result && typeof result === 'object' && 'meta' in result) {
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
meta: { changes: 1, duration: 0, last_row_id: 1 },
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
raw: async () => {
|
|
97
|
+
queries.push(query);
|
|
98
|
+
if (mockFn) {
|
|
99
|
+
const result = mockFn(query, boundValues);
|
|
100
|
+
if (Array.isArray(result)) {
|
|
101
|
+
// Convert objects to arrays of values
|
|
102
|
+
return result.map((row) => {
|
|
103
|
+
if (typeof row === 'object' && row !== null) {
|
|
104
|
+
return Object.values(row);
|
|
105
|
+
}
|
|
106
|
+
return row;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return [];
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return statement;
|
|
114
|
+
};
|
|
115
|
+
return {
|
|
116
|
+
prepare: createStatement,
|
|
117
|
+
batch: async (statements) => {
|
|
118
|
+
const results = [];
|
|
119
|
+
for (const stmt of statements) {
|
|
120
|
+
// Execute each statement's run method
|
|
121
|
+
await stmt.run();
|
|
122
|
+
results.push({ results: [], success: true, meta: createDefaultMeta() });
|
|
123
|
+
}
|
|
124
|
+
return results;
|
|
125
|
+
},
|
|
126
|
+
exec: async (query) => {
|
|
127
|
+
queries.push(query);
|
|
128
|
+
return { count: 1, duration: 0 };
|
|
129
|
+
},
|
|
130
|
+
dump: async () => {
|
|
131
|
+
return new ArrayBuffer(0);
|
|
132
|
+
},
|
|
133
|
+
_queries: queries,
|
|
134
|
+
_bindings: bindings,
|
|
135
|
+
_setupMock: (fn) => {
|
|
136
|
+
mockFn = fn;
|
|
137
|
+
},
|
|
138
|
+
_reset: () => {
|
|
139
|
+
queries.length = 0;
|
|
140
|
+
bindings.length = 0;
|
|
141
|
+
mockFn = undefined;
|
|
142
|
+
},
|
|
143
|
+
get _mockFn() {
|
|
144
|
+
return mockFn;
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=d1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d1.js","sourceRoot":"","sources":["../../src/cloudflare/d1.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Fetcher for testing Cloudflare Workers Service Bindings
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of the Fetcher interface used for
|
|
5
|
+
* Worker-to-Worker communication via Service Bindings.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const presetsApi = createMockFetcher();
|
|
10
|
+
*
|
|
11
|
+
* // Setup mock responses
|
|
12
|
+
* presetsApi._setupResponse('/api/v1/presets', {
|
|
13
|
+
* presets: [{ id: '1', name: 'Test Preset' }],
|
|
14
|
+
* total: 1,
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Use in tests
|
|
18
|
+
* const env = { PRESETS_API: presetsApi as unknown as Fetcher };
|
|
19
|
+
*
|
|
20
|
+
* // Assert calls were made
|
|
21
|
+
* expect(presetsApi._calls).toHaveLength(1);
|
|
22
|
+
* expect(presetsApi._calls[0].url).toContain('/api/v1/presets');
|
|
23
|
+
* expect(presetsApi._calls[0].method).toBe('GET');
|
|
24
|
+
*
|
|
25
|
+
* // Reset between tests
|
|
26
|
+
* presetsApi._reset();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Recorded fetch call for assertions
|
|
31
|
+
*/
|
|
32
|
+
export interface MockFetchCall {
|
|
33
|
+
url: string;
|
|
34
|
+
method: string;
|
|
35
|
+
headers: Record<string, string>;
|
|
36
|
+
body?: string;
|
|
37
|
+
timestamp: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Mock response configuration
|
|
41
|
+
*/
|
|
42
|
+
export interface MockResponseConfig {
|
|
43
|
+
status?: number;
|
|
44
|
+
headers?: Record<string, string>;
|
|
45
|
+
body?: unknown;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Extended mock Fetcher with test helpers
|
|
49
|
+
*/
|
|
50
|
+
export interface MockFetcher {
|
|
51
|
+
fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
52
|
+
/** Array of all fetch calls made (for assertions) */
|
|
53
|
+
_calls: MockFetchCall[];
|
|
54
|
+
/** Setup a response for a specific path pattern */
|
|
55
|
+
_setupResponse: (pathPattern: string | RegExp, response: unknown, config?: Omit<MockResponseConfig, 'body'>) => void;
|
|
56
|
+
/** Setup a custom response handler */
|
|
57
|
+
_setupHandler: (handler: (url: string, init?: RequestInit) => Promise<Response> | Response) => void;
|
|
58
|
+
/** Reset calls and response configurations */
|
|
59
|
+
_reset: () => void;
|
|
60
|
+
/** Set default response for unmatched requests */
|
|
61
|
+
_setDefaultResponse: (response: unknown, config?: Omit<MockResponseConfig, 'body'>) => void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates a mock Fetcher for testing Service Bindings
|
|
65
|
+
*
|
|
66
|
+
* The mock tracks all fetch calls and supports configuring
|
|
67
|
+
* responses for specific URL patterns.
|
|
68
|
+
*
|
|
69
|
+
* @returns A mock Fetcher that can be cast to Fetcher
|
|
70
|
+
*/
|
|
71
|
+
export declare function createMockFetcher(): MockFetcher;
|
|
72
|
+
//# sourceMappingURL=fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/cloudflare/fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE3E,qDAAqD;IACrD,MAAM,EAAE,aAAa,EAAE,CAAC;IAExB,mDAAmD;IACnD,cAAc,EAAE,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;IAErH,sCAAsC;IACtC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,KAAK,IAAI,CAAC;IAEpG,8CAA8C;IAC9C,MAAM,EAAE,MAAM,IAAI,CAAC;IAEnB,kDAAkD;IAClD,mBAAmB,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,KAAK,IAAI,CAAC;CAC7F;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAiH/C"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock Fetcher for testing Cloudflare Workers Service Bindings
|
|
3
|
+
*
|
|
4
|
+
* Provides a mock implementation of the Fetcher interface used for
|
|
5
|
+
* Worker-to-Worker communication via Service Bindings.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const presetsApi = createMockFetcher();
|
|
10
|
+
*
|
|
11
|
+
* // Setup mock responses
|
|
12
|
+
* presetsApi._setupResponse('/api/v1/presets', {
|
|
13
|
+
* presets: [{ id: '1', name: 'Test Preset' }],
|
|
14
|
+
* total: 1,
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // Use in tests
|
|
18
|
+
* const env = { PRESETS_API: presetsApi as unknown as Fetcher };
|
|
19
|
+
*
|
|
20
|
+
* // Assert calls were made
|
|
21
|
+
* expect(presetsApi._calls).toHaveLength(1);
|
|
22
|
+
* expect(presetsApi._calls[0].url).toContain('/api/v1/presets');
|
|
23
|
+
* expect(presetsApi._calls[0].method).toBe('GET');
|
|
24
|
+
*
|
|
25
|
+
* // Reset between tests
|
|
26
|
+
* presetsApi._reset();
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Creates a mock Fetcher for testing Service Bindings
|
|
31
|
+
*
|
|
32
|
+
* The mock tracks all fetch calls and supports configuring
|
|
33
|
+
* responses for specific URL patterns.
|
|
34
|
+
*
|
|
35
|
+
* @returns A mock Fetcher that can be cast to Fetcher
|
|
36
|
+
*/
|
|
37
|
+
export function createMockFetcher() {
|
|
38
|
+
const calls = [];
|
|
39
|
+
const responses = new Map();
|
|
40
|
+
let customHandler;
|
|
41
|
+
let defaultResponse = {
|
|
42
|
+
status: 200,
|
|
43
|
+
body: { success: true },
|
|
44
|
+
};
|
|
45
|
+
const createResponse = (config) => {
|
|
46
|
+
const { status = 200, headers = {}, body } = config;
|
|
47
|
+
const responseBody = body !== undefined ? JSON.stringify(body) : '';
|
|
48
|
+
return new Response(responseBody, {
|
|
49
|
+
status,
|
|
50
|
+
headers: {
|
|
51
|
+
'Content-Type': 'application/json',
|
|
52
|
+
...headers,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const findMatchingResponse = (url) => {
|
|
57
|
+
// First try exact string matches
|
|
58
|
+
for (const [pattern, config] of responses.entries()) {
|
|
59
|
+
if (typeof pattern === 'string' && url.includes(pattern)) {
|
|
60
|
+
return config;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Then try regex matches
|
|
64
|
+
for (const [pattern, config] of responses.entries()) {
|
|
65
|
+
if (pattern instanceof RegExp && pattern.test(url)) {
|
|
66
|
+
return config;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
fetch: async (input, init) => {
|
|
73
|
+
const url = input instanceof Request ? input.url : input.toString();
|
|
74
|
+
const method = init?.method ?? (input instanceof Request ? input.method : 'GET');
|
|
75
|
+
const headers = {};
|
|
76
|
+
// Extract headers
|
|
77
|
+
if (init?.headers) {
|
|
78
|
+
if (init.headers instanceof Headers) {
|
|
79
|
+
init.headers.forEach((value, key) => {
|
|
80
|
+
headers[key] = value;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
else if (Array.isArray(init.headers)) {
|
|
84
|
+
for (const [key, value] of init.headers) {
|
|
85
|
+
headers[key] = value;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
Object.assign(headers, init.headers);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Record the call
|
|
93
|
+
calls.push({
|
|
94
|
+
url,
|
|
95
|
+
method,
|
|
96
|
+
headers,
|
|
97
|
+
body: init?.body?.toString(),
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
});
|
|
100
|
+
// Use custom handler if provided
|
|
101
|
+
if (customHandler) {
|
|
102
|
+
return customHandler(url, init);
|
|
103
|
+
}
|
|
104
|
+
// Find matching response
|
|
105
|
+
const matchedConfig = findMatchingResponse(url);
|
|
106
|
+
if (matchedConfig) {
|
|
107
|
+
return createResponse(matchedConfig);
|
|
108
|
+
}
|
|
109
|
+
// Return default response
|
|
110
|
+
return createResponse(defaultResponse);
|
|
111
|
+
},
|
|
112
|
+
_calls: calls,
|
|
113
|
+
_setupResponse: (pathPattern, response, config) => {
|
|
114
|
+
responses.set(pathPattern, {
|
|
115
|
+
...config,
|
|
116
|
+
body: response,
|
|
117
|
+
});
|
|
118
|
+
},
|
|
119
|
+
_setupHandler: (handler) => {
|
|
120
|
+
customHandler = handler;
|
|
121
|
+
},
|
|
122
|
+
_reset: () => {
|
|
123
|
+
calls.length = 0;
|
|
124
|
+
responses.clear();
|
|
125
|
+
customHandler = undefined;
|
|
126
|
+
defaultResponse = { status: 200, body: { success: true } };
|
|
127
|
+
},
|
|
128
|
+
_setDefaultResponse: (response, config) => {
|
|
129
|
+
defaultResponse = {
|
|
130
|
+
...config,
|
|
131
|
+
body: response,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=fetcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetcher.js","sourceRoot":"","sources":["../../src/cloudflare/fetcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers mock implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides mocks for D1 Database, KV Namespace, R2 Bucket,
|
|
5
|
+
* Analytics Engine, and Service Binding Fetcher.
|
|
6
|
+
*/
|
|
7
|
+
export * from './d1.js';
|
|
8
|
+
export * from './kv.js';
|
|
9
|
+
export * from './fetcher.js';
|
|
10
|
+
export * from './r2.js';
|
|
11
|
+
export * from './analytics.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Workers mock implementations
|
|
3
|
+
*
|
|
4
|
+
* Provides mocks for D1 Database, KV Namespace, R2 Bucket,
|
|
5
|
+
* Analytics Engine, and Service Binding Fetcher.
|
|
6
|
+
*/
|
|
7
|
+
export * from './d1.js';
|
|
8
|
+
export * from './kv.js';
|
|
9
|
+
export * from './fetcher.js';
|
|
10
|
+
export * from './r2.js';
|
|
11
|
+
export * from './analytics.js';
|
|
12
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC;AACxB,cAAc,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock KV Namespace for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a Map-backed implementation of KVNamespace for testing
|
|
5
|
+
* rate limiting, caching, and other KV-dependent functionality.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const kv = createMockKV();
|
|
10
|
+
*
|
|
11
|
+
* // Use in tests
|
|
12
|
+
* const env = { RATE_LIMITS: kv as unknown as KVNamespace };
|
|
13
|
+
*
|
|
14
|
+
* // Pre-populate data
|
|
15
|
+
* await kv.put('user:123:count', '5');
|
|
16
|
+
*
|
|
17
|
+
* // Check stored values
|
|
18
|
+
* expect(kv._store.get('user:123:count')).toBe('5');
|
|
19
|
+
*
|
|
20
|
+
* // Check TTL tracking (if needed)
|
|
21
|
+
* expect(kv._ttls.get('user:123:count')).toBeGreaterThan(Date.now());
|
|
22
|
+
*
|
|
23
|
+
* // Reset between tests
|
|
24
|
+
* kv._reset();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* KV list result item
|
|
29
|
+
*/
|
|
30
|
+
interface KVListKey {
|
|
31
|
+
name: string;
|
|
32
|
+
expiration?: number;
|
|
33
|
+
metadata?: unknown;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* KV list result
|
|
37
|
+
*/
|
|
38
|
+
interface KVListResult {
|
|
39
|
+
keys: KVListKey[];
|
|
40
|
+
list_complete: boolean;
|
|
41
|
+
cursor?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Extended mock KV namespace with test helpers
|
|
45
|
+
*/
|
|
46
|
+
export interface MockKVNamespace {
|
|
47
|
+
get: (key: string, options?: {
|
|
48
|
+
type?: 'text' | 'json' | 'arrayBuffer' | 'stream';
|
|
49
|
+
}) => Promise<string | null>;
|
|
50
|
+
put: (key: string, value: string, options?: {
|
|
51
|
+
expirationTtl?: number;
|
|
52
|
+
expiration?: number;
|
|
53
|
+
metadata?: unknown;
|
|
54
|
+
}) => Promise<void>;
|
|
55
|
+
delete: (key: string) => Promise<void>;
|
|
56
|
+
list: (options?: {
|
|
57
|
+
prefix?: string;
|
|
58
|
+
limit?: number;
|
|
59
|
+
cursor?: string;
|
|
60
|
+
}) => Promise<KVListResult>;
|
|
61
|
+
getWithMetadata: <T = unknown>(key: string) => Promise<{
|
|
62
|
+
value: string | null;
|
|
63
|
+
metadata: T | null;
|
|
64
|
+
cacheStatus: null;
|
|
65
|
+
}>;
|
|
66
|
+
/** Internal storage map (for assertions) */
|
|
67
|
+
_store: Map<string, string>;
|
|
68
|
+
/** TTL tracking map - stores expiration timestamps */
|
|
69
|
+
_ttls: Map<string, number>;
|
|
70
|
+
/** Metadata tracking map */
|
|
71
|
+
_metadata: Map<string, unknown>;
|
|
72
|
+
/** Reset all storage */
|
|
73
|
+
_reset: () => void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Creates a mock KV namespace for testing
|
|
77
|
+
*
|
|
78
|
+
* The mock uses an in-memory Map for storage and tracks TTLs
|
|
79
|
+
* for expiration testing.
|
|
80
|
+
*
|
|
81
|
+
* @returns A mock KV namespace that can be cast to KVNamespace
|
|
82
|
+
*/
|
|
83
|
+
export declare function createMockKV(): MockKVNamespace;
|
|
84
|
+
export {};
|
|
85
|
+
//# sourceMappingURL=kv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../../src/cloudflare/kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH;;GAEG;AACH,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,UAAU,YAAY;IACpB,IAAI,EAAE,SAAS,EAAE,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9G,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClI,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;IAChG,eAAe,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC;QAAC,WAAW,EAAE,IAAI,CAAA;KAAE,CAAC,CAAC;IAExH,4CAA4C;IAC5C,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5B,sDAAsD;IACtD,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE3B,4BAA4B;IAC5B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAEhC,wBAAwB;IACxB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAmH9C"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock KV Namespace for testing Cloudflare Workers
|
|
3
|
+
*
|
|
4
|
+
* Provides a Map-backed implementation of KVNamespace for testing
|
|
5
|
+
* rate limiting, caching, and other KV-dependent functionality.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const kv = createMockKV();
|
|
10
|
+
*
|
|
11
|
+
* // Use in tests
|
|
12
|
+
* const env = { RATE_LIMITS: kv as unknown as KVNamespace };
|
|
13
|
+
*
|
|
14
|
+
* // Pre-populate data
|
|
15
|
+
* await kv.put('user:123:count', '5');
|
|
16
|
+
*
|
|
17
|
+
* // Check stored values
|
|
18
|
+
* expect(kv._store.get('user:123:count')).toBe('5');
|
|
19
|
+
*
|
|
20
|
+
* // Check TTL tracking (if needed)
|
|
21
|
+
* expect(kv._ttls.get('user:123:count')).toBeGreaterThan(Date.now());
|
|
22
|
+
*
|
|
23
|
+
* // Reset between tests
|
|
24
|
+
* kv._reset();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Creates a mock KV namespace for testing
|
|
29
|
+
*
|
|
30
|
+
* The mock uses an in-memory Map for storage and tracks TTLs
|
|
31
|
+
* for expiration testing.
|
|
32
|
+
*
|
|
33
|
+
* @returns A mock KV namespace that can be cast to KVNamespace
|
|
34
|
+
*/
|
|
35
|
+
export function createMockKV() {
|
|
36
|
+
const store = new Map();
|
|
37
|
+
const ttls = new Map();
|
|
38
|
+
const metadata = new Map();
|
|
39
|
+
/**
|
|
40
|
+
* Check if a key has expired
|
|
41
|
+
*/
|
|
42
|
+
const isExpired = (key) => {
|
|
43
|
+
const expiration = ttls.get(key);
|
|
44
|
+
if (expiration === undefined)
|
|
45
|
+
return false;
|
|
46
|
+
return Date.now() > expiration * 1000; // TTL is in seconds, convert to ms
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
get: async (key, options) => {
|
|
50
|
+
if (isExpired(key)) {
|
|
51
|
+
store.delete(key);
|
|
52
|
+
ttls.delete(key);
|
|
53
|
+
metadata.delete(key);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const value = store.get(key) ?? null;
|
|
57
|
+
if (value === null)
|
|
58
|
+
return null;
|
|
59
|
+
if (options?.type === 'json') {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(value);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
},
|
|
69
|
+
put: async (key, value, options) => {
|
|
70
|
+
store.set(key, value);
|
|
71
|
+
// Handle TTL
|
|
72
|
+
if (options?.expirationTtl) {
|
|
73
|
+
// expirationTtl is seconds from now
|
|
74
|
+
ttls.set(key, Math.floor(Date.now() / 1000) + options.expirationTtl);
|
|
75
|
+
}
|
|
76
|
+
else if (options?.expiration) {
|
|
77
|
+
// expiration is absolute Unix timestamp
|
|
78
|
+
ttls.set(key, options.expiration);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
ttls.delete(key);
|
|
82
|
+
}
|
|
83
|
+
// Handle metadata
|
|
84
|
+
if (options?.metadata !== undefined) {
|
|
85
|
+
metadata.set(key, options.metadata);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
delete: async (key) => {
|
|
89
|
+
store.delete(key);
|
|
90
|
+
ttls.delete(key);
|
|
91
|
+
metadata.delete(key);
|
|
92
|
+
},
|
|
93
|
+
list: async (options) => {
|
|
94
|
+
const keys = [];
|
|
95
|
+
const prefix = options?.prefix ?? '';
|
|
96
|
+
const limit = options?.limit ?? 1000;
|
|
97
|
+
for (const [key, _value] of store.entries()) {
|
|
98
|
+
if (key.startsWith(prefix) && !isExpired(key)) {
|
|
99
|
+
keys.push({
|
|
100
|
+
name: key,
|
|
101
|
+
expiration: ttls.get(key),
|
|
102
|
+
metadata: metadata.get(key),
|
|
103
|
+
});
|
|
104
|
+
if (keys.length >= limit) {
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
keys,
|
|
111
|
+
list_complete: keys.length < limit,
|
|
112
|
+
cursor: undefined,
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
getWithMetadata: async (key) => {
|
|
116
|
+
if (isExpired(key)) {
|
|
117
|
+
store.delete(key);
|
|
118
|
+
ttls.delete(key);
|
|
119
|
+
metadata.delete(key);
|
|
120
|
+
return { value: null, metadata: null, cacheStatus: null };
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
value: store.get(key) ?? null,
|
|
124
|
+
metadata: metadata.get(key) ?? null,
|
|
125
|
+
cacheStatus: null,
|
|
126
|
+
};
|
|
127
|
+
},
|
|
128
|
+
_store: store,
|
|
129
|
+
_ttls: ttls,
|
|
130
|
+
_metadata: metadata,
|
|
131
|
+
_reset: () => {
|
|
132
|
+
store.clear();
|
|
133
|
+
ttls.clear();
|
|
134
|
+
metadata.clear();
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=kv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.js","sourceRoot":"","sources":["../../src/cloudflare/kv.ts"],"names":[],"mappings":"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"}
|