@tinycloudlabs/sdk-services-test 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/dist/MockKVService.d.ts +145 -0
- package/dist/MockKVService.d.ts.map +1 -0
- package/dist/MockKVService.js +335 -0
- package/dist/MockKVService.js.map +1 -0
- package/dist/MockServiceContext.d.ts +160 -0
- package/dist/MockServiceContext.d.ts.map +1 -0
- package/dist/MockServiceContext.js +280 -0
- package/dist/MockServiceContext.js.map +1 -0
- package/dist/helpers.d.ts +219 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +314 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +1 -0
- package/package.json +38 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockServiceContext - Test implementation of IServiceContext
|
|
3
|
+
*
|
|
4
|
+
* Provides a configurable mock context for testing services
|
|
5
|
+
* without network dependencies.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* MockServiceContext implements IServiceContext for testing.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - In-memory service registry
|
|
12
|
+
* - Configurable mock invoke that returns test data
|
|
13
|
+
* - Configurable mock fetch with pattern matching
|
|
14
|
+
* - Event emission tracking for assertions
|
|
15
|
+
* - Session state management
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const context = new MockServiceContext({
|
|
20
|
+
* session: createMockSession(),
|
|
21
|
+
* fetchResponses: new Map([
|
|
22
|
+
* ['/invoke', { ok: true, status: 200, body: { data: 'test' } }],
|
|
23
|
+
* ]),
|
|
24
|
+
* });
|
|
25
|
+
*
|
|
26
|
+
* const kv = new KVService({});
|
|
27
|
+
* kv.initialize(context);
|
|
28
|
+
* context.registerService('kv', kv);
|
|
29
|
+
*
|
|
30
|
+
* // Assert events were emitted
|
|
31
|
+
* expect(context.getEmittedEvents('service.request')).toHaveLength(1);
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export class MockServiceContext {
|
|
35
|
+
constructor(config = {}) {
|
|
36
|
+
this._services = new Map();
|
|
37
|
+
this._eventHandlers = new Map();
|
|
38
|
+
this._emittedEvents = [];
|
|
39
|
+
this._abortController = new AbortController();
|
|
40
|
+
this._invokeCallCount = 0;
|
|
41
|
+
this._fetchCallCount = 0;
|
|
42
|
+
this._session = config.session ?? null;
|
|
43
|
+
this.hosts = config.hosts ?? ["https://mock.tinycloud.test"];
|
|
44
|
+
this._fetchResponses = config.fetchResponses ?? new Map();
|
|
45
|
+
this._defaultFetchResponse = config.defaultFetchResponse ?? {
|
|
46
|
+
ok: true,
|
|
47
|
+
status: 200,
|
|
48
|
+
statusText: "OK",
|
|
49
|
+
body: {},
|
|
50
|
+
};
|
|
51
|
+
this._invokeFunction = config.invoke ?? this.defaultInvoke.bind(this);
|
|
52
|
+
this.retryPolicy = config.retryPolicy ?? {
|
|
53
|
+
maxAttempts: 3,
|
|
54
|
+
backoff: "exponential",
|
|
55
|
+
baseDelayMs: 1000,
|
|
56
|
+
maxDelayMs: 10000,
|
|
57
|
+
retryableErrors: ["NETWORK_ERROR", "TIMEOUT"],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// ============================================================
|
|
61
|
+
// Session Management
|
|
62
|
+
// ============================================================
|
|
63
|
+
get session() {
|
|
64
|
+
return this._session;
|
|
65
|
+
}
|
|
66
|
+
get isAuthenticated() {
|
|
67
|
+
return this._session !== null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Set the session (test helper).
|
|
71
|
+
*/
|
|
72
|
+
setSession(session) {
|
|
73
|
+
this._session = session;
|
|
74
|
+
this.emit("session.changed", { authenticated: session !== null });
|
|
75
|
+
for (const service of this._services.values()) {
|
|
76
|
+
service.onSessionChange(session);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ============================================================
|
|
80
|
+
// Platform Dependencies
|
|
81
|
+
// ============================================================
|
|
82
|
+
/**
|
|
83
|
+
* Default invoke implementation that returns mock headers.
|
|
84
|
+
*/
|
|
85
|
+
defaultInvoke(session, service, path, action) {
|
|
86
|
+
return {
|
|
87
|
+
Authorization: `Bearer mock-token-${service}-${action}`,
|
|
88
|
+
"X-Mock-Service": service,
|
|
89
|
+
"X-Mock-Path": path,
|
|
90
|
+
"X-Mock-Action": action,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
get invoke() {
|
|
94
|
+
return (session, service, path, action) => {
|
|
95
|
+
this._invokeCallCount++;
|
|
96
|
+
return this._invokeFunction(session, service, path, action);
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
get fetch() {
|
|
100
|
+
return async (url, init) => {
|
|
101
|
+
this._fetchCallCount++;
|
|
102
|
+
// Find matching response
|
|
103
|
+
let mockResponse = this._defaultFetchResponse;
|
|
104
|
+
for (const [pattern, handler] of this._fetchResponses) {
|
|
105
|
+
const matches = typeof pattern === "string"
|
|
106
|
+
? url.includes(pattern)
|
|
107
|
+
: pattern.test(url);
|
|
108
|
+
if (matches) {
|
|
109
|
+
if (typeof handler === "function") {
|
|
110
|
+
mockResponse = await handler(url, init);
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
mockResponse = handler;
|
|
114
|
+
}
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Create mock response object
|
|
119
|
+
const headers = new Map(Object.entries(mockResponse.headers ?? {}).map(([k, v]) => [
|
|
120
|
+
k.toLowerCase(),
|
|
121
|
+
v,
|
|
122
|
+
]));
|
|
123
|
+
return {
|
|
124
|
+
ok: mockResponse.ok,
|
|
125
|
+
status: mockResponse.status,
|
|
126
|
+
statusText: mockResponse.statusText,
|
|
127
|
+
headers: {
|
|
128
|
+
get: (name) => headers.get(name.toLowerCase()) ?? null,
|
|
129
|
+
},
|
|
130
|
+
json: async () => mockResponse.body,
|
|
131
|
+
text: async () => typeof mockResponse.body === "string"
|
|
132
|
+
? mockResponse.body
|
|
133
|
+
: JSON.stringify(mockResponse.body),
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
// ============================================================
|
|
138
|
+
// Service Registry
|
|
139
|
+
// ============================================================
|
|
140
|
+
registerService(name, service) {
|
|
141
|
+
this._services.set(name, service);
|
|
142
|
+
}
|
|
143
|
+
unregisterService(name) {
|
|
144
|
+
this._services.delete(name);
|
|
145
|
+
}
|
|
146
|
+
getService(name) {
|
|
147
|
+
return this._services.get(name);
|
|
148
|
+
}
|
|
149
|
+
// ============================================================
|
|
150
|
+
// Event System (Telemetry)
|
|
151
|
+
// ============================================================
|
|
152
|
+
emit(event, data) {
|
|
153
|
+
// Record the event for assertions
|
|
154
|
+
this._emittedEvents.push({
|
|
155
|
+
event,
|
|
156
|
+
data,
|
|
157
|
+
timestamp: Date.now(),
|
|
158
|
+
});
|
|
159
|
+
// Notify handlers
|
|
160
|
+
const handlers = this._eventHandlers.get(event);
|
|
161
|
+
if (handlers) {
|
|
162
|
+
for (const handler of handlers) {
|
|
163
|
+
try {
|
|
164
|
+
handler(data);
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
// Swallow errors in handlers during tests
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
on(event, handler) {
|
|
173
|
+
if (!this._eventHandlers.has(event)) {
|
|
174
|
+
this._eventHandlers.set(event, new Set());
|
|
175
|
+
}
|
|
176
|
+
this._eventHandlers.get(event).add(handler);
|
|
177
|
+
return () => {
|
|
178
|
+
const handlers = this._eventHandlers.get(event);
|
|
179
|
+
if (handlers) {
|
|
180
|
+
handlers.delete(handler);
|
|
181
|
+
if (handlers.size === 0) {
|
|
182
|
+
this._eventHandlers.delete(event);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// ============================================================
|
|
188
|
+
// Lifecycle
|
|
189
|
+
// ============================================================
|
|
190
|
+
get abortSignal() {
|
|
191
|
+
return this._abortController.signal;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Abort all operations (test helper).
|
|
195
|
+
*/
|
|
196
|
+
abort() {
|
|
197
|
+
this._abortController.abort();
|
|
198
|
+
this._abortController = new AbortController();
|
|
199
|
+
for (const service of this._services.values()) {
|
|
200
|
+
service.onSignOut();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// ============================================================
|
|
204
|
+
// Test Assertion Helpers
|
|
205
|
+
// ============================================================
|
|
206
|
+
/**
|
|
207
|
+
* Get all emitted events.
|
|
208
|
+
*/
|
|
209
|
+
getEmittedEvents() {
|
|
210
|
+
return [...this._emittedEvents];
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Get emitted events filtered by event name.
|
|
214
|
+
*/
|
|
215
|
+
getEmittedEventsByName(eventName) {
|
|
216
|
+
return this._emittedEvents.filter((e) => e.event === eventName);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Check if a specific event was emitted.
|
|
220
|
+
*/
|
|
221
|
+
wasEventEmitted(eventName) {
|
|
222
|
+
return this._emittedEvents.some((e) => e.event === eventName);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get the count of events by name.
|
|
226
|
+
*/
|
|
227
|
+
getEventCount(eventName) {
|
|
228
|
+
return this._emittedEvents.filter((e) => e.event === eventName).length;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Clear all recorded events.
|
|
232
|
+
*/
|
|
233
|
+
clearEmittedEvents() {
|
|
234
|
+
this._emittedEvents = [];
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Get the number of invoke calls.
|
|
238
|
+
*/
|
|
239
|
+
get invokeCallCount() {
|
|
240
|
+
return this._invokeCallCount;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the number of fetch calls.
|
|
244
|
+
*/
|
|
245
|
+
get fetchCallCount() {
|
|
246
|
+
return this._fetchCallCount;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Reset call counts.
|
|
250
|
+
*/
|
|
251
|
+
resetCallCounts() {
|
|
252
|
+
this._invokeCallCount = 0;
|
|
253
|
+
this._fetchCallCount = 0;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Add a mock fetch response for a URL pattern.
|
|
257
|
+
*/
|
|
258
|
+
addFetchResponse(pattern, response) {
|
|
259
|
+
this._fetchResponses.set(pattern, response);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Remove a mock fetch response.
|
|
263
|
+
*/
|
|
264
|
+
removeFetchResponse(pattern) {
|
|
265
|
+
this._fetchResponses.delete(pattern);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Clear all mock fetch responses.
|
|
269
|
+
*/
|
|
270
|
+
clearFetchResponses() {
|
|
271
|
+
this._fetchResponses.clear();
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Set the default fetch response.
|
|
275
|
+
*/
|
|
276
|
+
setDefaultFetchResponse(response) {
|
|
277
|
+
this._defaultFetchResponse = response;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=MockServiceContext.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockServiceContext.js","sourceRoot":"","sources":["../src/MockServiceContext.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Utilities for SDK Services
|
|
3
|
+
*
|
|
4
|
+
* Factory functions and helpers for creating test fixtures.
|
|
5
|
+
*/
|
|
6
|
+
import type { ServiceSession } from "@tinycloudlabs/sdk-services";
|
|
7
|
+
import { MockServiceContext, MockServiceContextConfig, MockFetchResponse } from "./MockServiceContext";
|
|
8
|
+
import { MockKVService, MockKVServiceConfig } from "./MockKVService";
|
|
9
|
+
/**
|
|
10
|
+
* Options for creating a test context.
|
|
11
|
+
*/
|
|
12
|
+
export interface CreateTestContextOptions extends MockServiceContextConfig {
|
|
13
|
+
/** Whether to include a default authenticated session */
|
|
14
|
+
authenticated?: boolean;
|
|
15
|
+
/** Custom session data */
|
|
16
|
+
sessionOverrides?: Partial<ServiceSession>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a mock ServiceSession for testing.
|
|
20
|
+
*
|
|
21
|
+
* @param overrides - Optional overrides for session fields
|
|
22
|
+
* @returns A mock ServiceSession
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const session = createMockSession();
|
|
27
|
+
* const customSession = createMockSession({
|
|
28
|
+
* spaceId: 'space:custom-id',
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function createMockSession(overrides?: Partial<ServiceSession>): ServiceSession;
|
|
33
|
+
/**
|
|
34
|
+
* Create a MockServiceContext with sensible defaults for testing.
|
|
35
|
+
*
|
|
36
|
+
* @param options - Configuration options
|
|
37
|
+
* @returns A configured MockServiceContext
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* // Create an authenticated context
|
|
42
|
+
* const context = createTestContext({ authenticated: true });
|
|
43
|
+
*
|
|
44
|
+
* // Create with custom fetch responses
|
|
45
|
+
* const context = createTestContext({
|
|
46
|
+
* authenticated: true,
|
|
47
|
+
* fetchResponses: new Map([
|
|
48
|
+
* ['/invoke', { ok: true, status: 200, body: { data: 'test' } }],
|
|
49
|
+
* ]),
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createTestContext(options?: CreateTestContextOptions): MockServiceContext;
|
|
54
|
+
/**
|
|
55
|
+
* Create a MockKVService with initial data.
|
|
56
|
+
*
|
|
57
|
+
* @param initialData - Key-value pairs to seed the store
|
|
58
|
+
* @param config - Additional configuration
|
|
59
|
+
* @returns A configured MockKVService
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const kv = createMockKV({
|
|
64
|
+
* 'user/settings': { theme: 'dark' },
|
|
65
|
+
* 'user/profile': { name: 'Alice' },
|
|
66
|
+
* });
|
|
67
|
+
*
|
|
68
|
+
* // With latency simulation
|
|
69
|
+
* const kv = createMockKV(
|
|
70
|
+
* { 'key': 'value' },
|
|
71
|
+
* { latencyMs: 50 }
|
|
72
|
+
* );
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function createMockKV(initialData?: Record<string, unknown>, config?: Omit<MockKVServiceConfig, "initialData">): MockKVService;
|
|
76
|
+
/**
|
|
77
|
+
* Wait for a specific event to be emitted from a context.
|
|
78
|
+
*
|
|
79
|
+
* @param context - The MockServiceContext to listen on
|
|
80
|
+
* @param eventName - The event name to wait for
|
|
81
|
+
* @param timeoutMs - Timeout in milliseconds (default: 5000)
|
|
82
|
+
* @returns Promise that resolves with the event data
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```typescript
|
|
86
|
+
* // Wait for session change event
|
|
87
|
+
* const eventPromise = waitForEvent(context, 'session.changed');
|
|
88
|
+
* context.setSession(createMockSession());
|
|
89
|
+
* const eventData = await eventPromise;
|
|
90
|
+
* expect(eventData.authenticated).toBe(true);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
export declare function waitForEvent(context: MockServiceContext, eventName: string, timeoutMs?: number): Promise<unknown>;
|
|
94
|
+
/**
|
|
95
|
+
* Wait for multiple events to be emitted.
|
|
96
|
+
*
|
|
97
|
+
* @param context - The MockServiceContext to listen on
|
|
98
|
+
* @param eventName - The event name to wait for
|
|
99
|
+
* @param count - Number of events to wait for
|
|
100
|
+
* @param timeoutMs - Timeout in milliseconds (default: 5000)
|
|
101
|
+
* @returns Promise that resolves with array of event data
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* // Wait for 3 service requests
|
|
106
|
+
* const eventsPromise = waitForEvents(context, 'service.request', 3);
|
|
107
|
+
* await kv.get('key1');
|
|
108
|
+
* await kv.get('key2');
|
|
109
|
+
* await kv.get('key3');
|
|
110
|
+
* const events = await eventsPromise;
|
|
111
|
+
* expect(events).toHaveLength(3);
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export declare function waitForEvents(context: MockServiceContext, eventName: string, count: number, timeoutMs?: number): Promise<unknown[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Create a mock fetch response for success scenarios.
|
|
117
|
+
*
|
|
118
|
+
* @param body - Response body
|
|
119
|
+
* @param headers - Optional response headers
|
|
120
|
+
* @returns MockFetchResponse
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const response = mockOkResponse({ keys: ['a', 'b'] });
|
|
125
|
+
* context.addFetchResponse('/invoke', response);
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
export declare function mockOkResponse(body: unknown, headers?: Record<string, string>): MockFetchResponse;
|
|
129
|
+
/**
|
|
130
|
+
* Create a mock fetch response for error scenarios.
|
|
131
|
+
*
|
|
132
|
+
* @param status - HTTP status code
|
|
133
|
+
* @param body - Error body
|
|
134
|
+
* @param statusText - Optional status text
|
|
135
|
+
* @returns MockFetchResponse
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```typescript
|
|
139
|
+
* const response = mockErrorResponse(404, { error: 'Not found' });
|
|
140
|
+
* context.addFetchResponse('/invoke', response);
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
export declare function mockErrorResponse(status: number, body?: unknown, statusText?: string): MockFetchResponse;
|
|
144
|
+
/**
|
|
145
|
+
* Create a delayed mock fetch handler for testing latency.
|
|
146
|
+
*
|
|
147
|
+
* @param response - The response to return after delay
|
|
148
|
+
* @param delayMs - Delay in milliseconds
|
|
149
|
+
* @returns Mock fetch handler function
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* context.addFetchResponse(
|
|
154
|
+
* '/invoke',
|
|
155
|
+
* delayedResponse(mockOkResponse({ data: 'test' }), 100)
|
|
156
|
+
* );
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
export declare function delayedResponse(response: MockFetchResponse, delayMs: number): () => Promise<MockFetchResponse>;
|
|
160
|
+
/**
|
|
161
|
+
* Create a sequence of mock responses that cycle through.
|
|
162
|
+
*
|
|
163
|
+
* @param responses - Array of responses to cycle through
|
|
164
|
+
* @returns Mock fetch handler function
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* // First call returns error, subsequent calls succeed
|
|
169
|
+
* context.addFetchResponse(
|
|
170
|
+
* '/invoke',
|
|
171
|
+
* sequenceResponses([
|
|
172
|
+
* mockErrorResponse(503, 'Temporarily unavailable'),
|
|
173
|
+
* mockOkResponse({ data: 'success' }),
|
|
174
|
+
* ])
|
|
175
|
+
* );
|
|
176
|
+
* ```
|
|
177
|
+
*/
|
|
178
|
+
export declare function sequenceResponses(responses: MockFetchResponse[]): () => MockFetchResponse;
|
|
179
|
+
/**
|
|
180
|
+
* Assert that an operation was recorded with specific properties.
|
|
181
|
+
*
|
|
182
|
+
* @param kv - The MockKVService to check
|
|
183
|
+
* @param expected - Expected operation properties
|
|
184
|
+
* @returns Whether a matching operation was found
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```typescript
|
|
188
|
+
* await kv.put('key', 'value');
|
|
189
|
+
* expect(wasOperationRecorded(kv, { type: 'put', key: 'key' })).toBe(true);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export declare function wasOperationRecorded(kv: MockKVService, expected: Partial<{
|
|
193
|
+
type: "get" | "put" | "list" | "delete" | "head";
|
|
194
|
+
key: string;
|
|
195
|
+
value: unknown;
|
|
196
|
+
}>): boolean;
|
|
197
|
+
/**
|
|
198
|
+
* Setup a MockKVService with a MockServiceContext.
|
|
199
|
+
*
|
|
200
|
+
* @param kvConfig - MockKVService configuration
|
|
201
|
+
* @param contextConfig - MockServiceContext configuration
|
|
202
|
+
* @returns Object with initialized kv and context
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* ```typescript
|
|
206
|
+
* const { kv, context } = setupMockKV(
|
|
207
|
+
* { initialData: { 'key': 'value' } },
|
|
208
|
+
* { authenticated: true }
|
|
209
|
+
* );
|
|
210
|
+
*
|
|
211
|
+
* const result = await kv.get('key');
|
|
212
|
+
* expect(result.ok).toBe(true);
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
export declare function setupMockKV(kvConfig?: MockKVServiceConfig, contextConfig?: CreateTestContextOptions): {
|
|
216
|
+
kv: MockKVService;
|
|
217
|
+
context: MockServiceContext;
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EACL,kBAAkB,EAClB,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,0BAA0B;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAClC,cAAc,CAgBhB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,kBAAkB,CAapB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,MAAM,CAAC,EAAE,IAAI,CAAC,mBAAmB,EAAE,aAAa,CAAC,GAChD,aAAa,CAKf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAC3B,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,OAAO,EAAE,CAAC,CAsBpB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,iBAAiB,CAWnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,OAAO,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,iBAAiB,CAUnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,iBAAiB,EAC3B,OAAO,EAAE,MAAM,GACd,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAKlC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,iBAAiB,EAAE,GAC7B,MAAM,iBAAiB,CASzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,EAAE,EAAE,aAAa,EACjB,QAAQ,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC,GACD,OAAO,CAST;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CACzB,QAAQ,CAAC,EAAE,mBAAmB,EAC9B,aAAa,CAAC,EAAE,wBAAwB,GACvC;IAAE,EAAE,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE,CAMpD"}
|