@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,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockKVService - Test implementation of IKVService
|
|
3
|
+
*
|
|
4
|
+
* Provides an in-memory KV service for testing without network dependencies.
|
|
5
|
+
*/
|
|
6
|
+
import type { IKVService, IPrefixedKVService, IServiceContext, ServiceSession, Result, KVServiceConfig, KVGetOptions, KVPutOptions, KVListOptions, KVDeleteOptions, KVHeadOptions, KVResponse, KVListResponse } from "@tinycloudlabs/sdk-services";
|
|
7
|
+
/**
|
|
8
|
+
* Recorded operation for assertions.
|
|
9
|
+
*/
|
|
10
|
+
export interface RecordedOperation {
|
|
11
|
+
type: "get" | "put" | "list" | "delete" | "head";
|
|
12
|
+
key?: string;
|
|
13
|
+
value?: unknown;
|
|
14
|
+
options?: unknown;
|
|
15
|
+
timestamp: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Error injection configuration.
|
|
19
|
+
*/
|
|
20
|
+
export interface ErrorInjection {
|
|
21
|
+
/** Key pattern to match (string for exact match, RegExp for pattern) */
|
|
22
|
+
pattern: string | RegExp;
|
|
23
|
+
/** Error code to return */
|
|
24
|
+
code: string;
|
|
25
|
+
/** Error message */
|
|
26
|
+
message: string;
|
|
27
|
+
/** Operation types to inject error on */
|
|
28
|
+
operations?: Array<"get" | "put" | "list" | "delete" | "head">;
|
|
29
|
+
/** Number of times to inject (undefined = always) */
|
|
30
|
+
count?: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Configuration for MockKVService.
|
|
34
|
+
*/
|
|
35
|
+
export interface MockKVServiceConfig extends KVServiceConfig {
|
|
36
|
+
/** Initial data to populate the store */
|
|
37
|
+
initialData?: Record<string, unknown>;
|
|
38
|
+
/** Simulated latency in milliseconds (0 = instant) */
|
|
39
|
+
latencyMs?: number;
|
|
40
|
+
/** Error injections for testing error handling */
|
|
41
|
+
errorInjections?: ErrorInjection[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* MockKVService implements IKVService for testing.
|
|
45
|
+
*
|
|
46
|
+
* Features:
|
|
47
|
+
* - In-memory key-value store
|
|
48
|
+
* - Configurable latency simulation
|
|
49
|
+
* - Error injection capability
|
|
50
|
+
* - Operation tracking for assertions
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const mockKV = new MockKVService({
|
|
55
|
+
* initialData: { 'user/settings': { theme: 'dark' } },
|
|
56
|
+
* latencyMs: 10,
|
|
57
|
+
* });
|
|
58
|
+
* mockKV.initialize(context);
|
|
59
|
+
*
|
|
60
|
+
* const result = await mockKV.get('user/settings');
|
|
61
|
+
* expect(result.ok).toBe(true);
|
|
62
|
+
* expect(result.data.data).toEqual({ theme: 'dark' });
|
|
63
|
+
*
|
|
64
|
+
* // Check operations
|
|
65
|
+
* expect(mockKV.getOperations('get')).toHaveLength(1);
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare class MockKVService implements IKVService {
|
|
69
|
+
static readonly serviceName = "kv";
|
|
70
|
+
private _config;
|
|
71
|
+
private _context;
|
|
72
|
+
private _store;
|
|
73
|
+
private _operations;
|
|
74
|
+
private _errorInjections;
|
|
75
|
+
private _latencyMs;
|
|
76
|
+
private _abortController;
|
|
77
|
+
constructor(config?: MockKVServiceConfig);
|
|
78
|
+
get config(): KVServiceConfig;
|
|
79
|
+
initialize(context: IServiceContext): void;
|
|
80
|
+
onSessionChange(session: ServiceSession | null): void;
|
|
81
|
+
onSignOut(): void;
|
|
82
|
+
get<T = unknown>(key: string, options?: KVGetOptions): Promise<Result<KVResponse<T>>>;
|
|
83
|
+
put(key: string, value: unknown, options?: KVPutOptions): Promise<Result<KVResponse<void>>>;
|
|
84
|
+
list(options?: KVListOptions): Promise<Result<KVListResponse>>;
|
|
85
|
+
delete(key: string, options?: KVDeleteOptions): Promise<Result<void>>;
|
|
86
|
+
head(key: string, options?: KVHeadOptions): Promise<Result<KVResponse<void>>>;
|
|
87
|
+
/**
|
|
88
|
+
* Create a prefix-scoped view of this KV service.
|
|
89
|
+
*
|
|
90
|
+
* @param prefix - The prefix to apply to all operations
|
|
91
|
+
* @returns A PrefixedKVService scoped to the prefix
|
|
92
|
+
*/
|
|
93
|
+
withPrefix(prefix: string): IPrefixedKVService;
|
|
94
|
+
/**
|
|
95
|
+
* Get all recorded operations.
|
|
96
|
+
*/
|
|
97
|
+
getOperations(): RecordedOperation[];
|
|
98
|
+
/**
|
|
99
|
+
* Get operations filtered by type.
|
|
100
|
+
*/
|
|
101
|
+
getOperationsByType(type: RecordedOperation["type"]): RecordedOperation[];
|
|
102
|
+
/**
|
|
103
|
+
* Clear recorded operations.
|
|
104
|
+
*/
|
|
105
|
+
clearOperations(): void;
|
|
106
|
+
/**
|
|
107
|
+
* Get the in-memory store contents.
|
|
108
|
+
*/
|
|
109
|
+
getStoreContents(): Map<string, unknown>;
|
|
110
|
+
/**
|
|
111
|
+
* Directly set a value in the store (bypasses operation recording).
|
|
112
|
+
*/
|
|
113
|
+
setStoreValue(key: string, value: unknown): void;
|
|
114
|
+
/**
|
|
115
|
+
* Clear the entire store.
|
|
116
|
+
*/
|
|
117
|
+
clearStore(): void;
|
|
118
|
+
/**
|
|
119
|
+
* Add an error injection.
|
|
120
|
+
*/
|
|
121
|
+
addErrorInjection(injection: ErrorInjection): void;
|
|
122
|
+
/**
|
|
123
|
+
* Clear all error injections.
|
|
124
|
+
*/
|
|
125
|
+
clearErrorInjections(): void;
|
|
126
|
+
/**
|
|
127
|
+
* Set the simulated latency.
|
|
128
|
+
*/
|
|
129
|
+
setLatency(ms: number): void;
|
|
130
|
+
/**
|
|
131
|
+
* Get the number of items in the store.
|
|
132
|
+
*/
|
|
133
|
+
get storeSize(): number;
|
|
134
|
+
/**
|
|
135
|
+
* Check if a key exists in the store.
|
|
136
|
+
*/
|
|
137
|
+
hasKey(key: string): boolean;
|
|
138
|
+
private getFullKey;
|
|
139
|
+
private createStoredValue;
|
|
140
|
+
private createHeaders;
|
|
141
|
+
private recordOperation;
|
|
142
|
+
private checkErrorInjection;
|
|
143
|
+
private simulateLatency;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=MockKVService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockKVService.d.ts","sourceRoot":"","sources":["../src/MockKVService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockKVService - Test implementation of IKVService
|
|
3
|
+
*
|
|
4
|
+
* Provides an in-memory KV service for testing without network dependencies.
|
|
5
|
+
*/
|
|
6
|
+
import { ok, err, ErrorCodes, serviceError, PrefixedKVService } from "@tinycloudlabs/sdk-services";
|
|
7
|
+
/**
|
|
8
|
+
* MockKVService implements IKVService for testing.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - In-memory key-value store
|
|
12
|
+
* - Configurable latency simulation
|
|
13
|
+
* - Error injection capability
|
|
14
|
+
* - Operation tracking for assertions
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const mockKV = new MockKVService({
|
|
19
|
+
* initialData: { 'user/settings': { theme: 'dark' } },
|
|
20
|
+
* latencyMs: 10,
|
|
21
|
+
* });
|
|
22
|
+
* mockKV.initialize(context);
|
|
23
|
+
*
|
|
24
|
+
* const result = await mockKV.get('user/settings');
|
|
25
|
+
* expect(result.ok).toBe(true);
|
|
26
|
+
* expect(result.data.data).toEqual({ theme: 'dark' });
|
|
27
|
+
*
|
|
28
|
+
* // Check operations
|
|
29
|
+
* expect(mockKV.getOperations('get')).toHaveLength(1);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class MockKVService {
|
|
33
|
+
constructor(config = {}) {
|
|
34
|
+
this._store = new Map();
|
|
35
|
+
this._operations = [];
|
|
36
|
+
this._abortController = new AbortController();
|
|
37
|
+
this._config = config;
|
|
38
|
+
this._latencyMs = config.latencyMs ?? 0;
|
|
39
|
+
this._errorInjections = [...(config.errorInjections ?? [])];
|
|
40
|
+
// Initialize with seed data
|
|
41
|
+
if (config.initialData) {
|
|
42
|
+
for (const [key, value] of Object.entries(config.initialData)) {
|
|
43
|
+
this._store.set(key, this.createStoredValue(value));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
get config() {
|
|
48
|
+
return this._config;
|
|
49
|
+
}
|
|
50
|
+
initialize(context) {
|
|
51
|
+
this._context = context;
|
|
52
|
+
}
|
|
53
|
+
onSessionChange(session) {
|
|
54
|
+
// No-op for mock
|
|
55
|
+
}
|
|
56
|
+
onSignOut() {
|
|
57
|
+
this._abortController.abort();
|
|
58
|
+
this._abortController = new AbortController();
|
|
59
|
+
}
|
|
60
|
+
// ============================================================
|
|
61
|
+
// KV Operations
|
|
62
|
+
// ============================================================
|
|
63
|
+
async get(key, options) {
|
|
64
|
+
this.recordOperation("get", key, undefined, options);
|
|
65
|
+
const fullKey = this.getFullKey(key, options?.prefix);
|
|
66
|
+
// Check for error injection
|
|
67
|
+
const injectedError = this.checkErrorInjection(fullKey, "get");
|
|
68
|
+
if (injectedError) {
|
|
69
|
+
return err(injectedError);
|
|
70
|
+
}
|
|
71
|
+
await this.simulateLatency();
|
|
72
|
+
// Check abort
|
|
73
|
+
if (options?.signal?.aborted) {
|
|
74
|
+
return err(serviceError(ErrorCodes.ABORTED, "Request aborted", "kv"));
|
|
75
|
+
}
|
|
76
|
+
const stored = this._store.get(fullKey);
|
|
77
|
+
if (!stored) {
|
|
78
|
+
return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, "kv"));
|
|
79
|
+
}
|
|
80
|
+
return ok({
|
|
81
|
+
data: stored.data,
|
|
82
|
+
headers: this.createHeaders(stored),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async put(key, value, options) {
|
|
86
|
+
this.recordOperation("put", key, value, options);
|
|
87
|
+
const fullKey = this.getFullKey(key, options?.prefix);
|
|
88
|
+
// Check for error injection
|
|
89
|
+
const injectedError = this.checkErrorInjection(fullKey, "put");
|
|
90
|
+
if (injectedError) {
|
|
91
|
+
return err(injectedError);
|
|
92
|
+
}
|
|
93
|
+
await this.simulateLatency();
|
|
94
|
+
// Check abort
|
|
95
|
+
if (options?.signal?.aborted) {
|
|
96
|
+
return err(serviceError(ErrorCodes.ABORTED, "Request aborted", "kv"));
|
|
97
|
+
}
|
|
98
|
+
const stored = this.createStoredValue(value, options?.contentType);
|
|
99
|
+
this._store.set(fullKey, stored);
|
|
100
|
+
return ok({
|
|
101
|
+
data: undefined,
|
|
102
|
+
headers: this.createHeaders(stored),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async list(options) {
|
|
106
|
+
this.recordOperation("list", options?.prefix ?? options?.path, undefined, options);
|
|
107
|
+
// Build prefix
|
|
108
|
+
let prefix = options?.prefix ?? this._config.prefix ?? "";
|
|
109
|
+
if (options?.path) {
|
|
110
|
+
prefix = prefix ? `${prefix}/${options.path}` : options.path;
|
|
111
|
+
}
|
|
112
|
+
// Check for error injection
|
|
113
|
+
const injectedError = this.checkErrorInjection(prefix, "list");
|
|
114
|
+
if (injectedError) {
|
|
115
|
+
return err(injectedError);
|
|
116
|
+
}
|
|
117
|
+
await this.simulateLatency();
|
|
118
|
+
// Check abort
|
|
119
|
+
if (options?.signal?.aborted) {
|
|
120
|
+
return err(serviceError(ErrorCodes.ABORTED, "Request aborted", "kv"));
|
|
121
|
+
}
|
|
122
|
+
let keys = Array.from(this._store.keys());
|
|
123
|
+
// Filter by prefix
|
|
124
|
+
if (prefix) {
|
|
125
|
+
const prefixWithSlash = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
126
|
+
keys = keys.filter((k) => k === prefix || k.startsWith(prefixWithSlash));
|
|
127
|
+
}
|
|
128
|
+
// Optionally remove prefix
|
|
129
|
+
if (options?.removePrefix && prefix) {
|
|
130
|
+
const prefixWithSlash = prefix.endsWith("/") ? prefix : `${prefix}/`;
|
|
131
|
+
keys = keys.map((k) => k.startsWith(prefixWithSlash) ? k.slice(prefixWithSlash.length) : k);
|
|
132
|
+
}
|
|
133
|
+
return ok({ keys });
|
|
134
|
+
}
|
|
135
|
+
async delete(key, options) {
|
|
136
|
+
this.recordOperation("delete", key, undefined, options);
|
|
137
|
+
const fullKey = this.getFullKey(key, options?.prefix);
|
|
138
|
+
// Check for error injection
|
|
139
|
+
const injectedError = this.checkErrorInjection(fullKey, "delete");
|
|
140
|
+
if (injectedError) {
|
|
141
|
+
return err(injectedError);
|
|
142
|
+
}
|
|
143
|
+
await this.simulateLatency();
|
|
144
|
+
// Check abort
|
|
145
|
+
if (options?.signal?.aborted) {
|
|
146
|
+
return err(serviceError(ErrorCodes.ABORTED, "Request aborted", "kv"));
|
|
147
|
+
}
|
|
148
|
+
if (!this._store.has(fullKey)) {
|
|
149
|
+
return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, "kv"));
|
|
150
|
+
}
|
|
151
|
+
this._store.delete(fullKey);
|
|
152
|
+
return ok(undefined);
|
|
153
|
+
}
|
|
154
|
+
async head(key, options) {
|
|
155
|
+
this.recordOperation("head", key, undefined, options);
|
|
156
|
+
const fullKey = this.getFullKey(key, options?.prefix);
|
|
157
|
+
// Check for error injection
|
|
158
|
+
const injectedError = this.checkErrorInjection(fullKey, "head");
|
|
159
|
+
if (injectedError) {
|
|
160
|
+
return err(injectedError);
|
|
161
|
+
}
|
|
162
|
+
await this.simulateLatency();
|
|
163
|
+
// Check abort
|
|
164
|
+
if (options?.signal?.aborted) {
|
|
165
|
+
return err(serviceError(ErrorCodes.ABORTED, "Request aborted", "kv"));
|
|
166
|
+
}
|
|
167
|
+
const stored = this._store.get(fullKey);
|
|
168
|
+
if (!stored) {
|
|
169
|
+
return err(serviceError(ErrorCodes.KV_NOT_FOUND, `Key not found: ${key}`, "kv"));
|
|
170
|
+
}
|
|
171
|
+
return ok({
|
|
172
|
+
data: undefined,
|
|
173
|
+
headers: this.createHeaders(stored),
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Create a prefix-scoped view of this KV service.
|
|
178
|
+
*
|
|
179
|
+
* @param prefix - The prefix to apply to all operations
|
|
180
|
+
* @returns A PrefixedKVService scoped to the prefix
|
|
181
|
+
*/
|
|
182
|
+
withPrefix(prefix) {
|
|
183
|
+
return new PrefixedKVService(this, prefix);
|
|
184
|
+
}
|
|
185
|
+
// ============================================================
|
|
186
|
+
// Test Helpers
|
|
187
|
+
// ============================================================
|
|
188
|
+
/**
|
|
189
|
+
* Get all recorded operations.
|
|
190
|
+
*/
|
|
191
|
+
getOperations() {
|
|
192
|
+
return [...this._operations];
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get operations filtered by type.
|
|
196
|
+
*/
|
|
197
|
+
getOperationsByType(type) {
|
|
198
|
+
return this._operations.filter((op) => op.type === type);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Clear recorded operations.
|
|
202
|
+
*/
|
|
203
|
+
clearOperations() {
|
|
204
|
+
this._operations = [];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get the in-memory store contents.
|
|
208
|
+
*/
|
|
209
|
+
getStoreContents() {
|
|
210
|
+
const result = new Map();
|
|
211
|
+
for (const [key, stored] of this._store) {
|
|
212
|
+
result.set(key, stored.data);
|
|
213
|
+
}
|
|
214
|
+
return result;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Directly set a value in the store (bypasses operation recording).
|
|
218
|
+
*/
|
|
219
|
+
setStoreValue(key, value) {
|
|
220
|
+
this._store.set(key, this.createStoredValue(value));
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Clear the entire store.
|
|
224
|
+
*/
|
|
225
|
+
clearStore() {
|
|
226
|
+
this._store.clear();
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Add an error injection.
|
|
230
|
+
*/
|
|
231
|
+
addErrorInjection(injection) {
|
|
232
|
+
this._errorInjections.push(injection);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Clear all error injections.
|
|
236
|
+
*/
|
|
237
|
+
clearErrorInjections() {
|
|
238
|
+
this._errorInjections = [];
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Set the simulated latency.
|
|
242
|
+
*/
|
|
243
|
+
setLatency(ms) {
|
|
244
|
+
this._latencyMs = ms;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get the number of items in the store.
|
|
248
|
+
*/
|
|
249
|
+
get storeSize() {
|
|
250
|
+
return this._store.size;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Check if a key exists in the store.
|
|
254
|
+
*/
|
|
255
|
+
hasKey(key) {
|
|
256
|
+
return this._store.has(key);
|
|
257
|
+
}
|
|
258
|
+
// ============================================================
|
|
259
|
+
// Private Helpers
|
|
260
|
+
// ============================================================
|
|
261
|
+
getFullKey(key, prefixOverride) {
|
|
262
|
+
const prefix = prefixOverride ?? this._config.prefix ?? "";
|
|
263
|
+
return prefix ? `${prefix}/${key}` : key;
|
|
264
|
+
}
|
|
265
|
+
createStoredValue(data, contentType) {
|
|
266
|
+
return {
|
|
267
|
+
data,
|
|
268
|
+
contentType: contentType ?? "application/json",
|
|
269
|
+
lastModified: new Date(),
|
|
270
|
+
etag: `"${Date.now()}-${Math.random().toString(36).slice(2)}"`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
createHeaders(stored) {
|
|
274
|
+
const headersMap = new Map([
|
|
275
|
+
["etag", stored.etag],
|
|
276
|
+
["content-type", stored.contentType],
|
|
277
|
+
["last-modified", stored.lastModified.toISOString()],
|
|
278
|
+
[
|
|
279
|
+
"content-length",
|
|
280
|
+
String(typeof stored.data === "string"
|
|
281
|
+
? stored.data.length
|
|
282
|
+
: JSON.stringify(stored.data).length),
|
|
283
|
+
],
|
|
284
|
+
]);
|
|
285
|
+
return {
|
|
286
|
+
etag: stored.etag,
|
|
287
|
+
contentType: stored.contentType,
|
|
288
|
+
lastModified: stored.lastModified.toISOString(),
|
|
289
|
+
contentLength: typeof stored.data === "string"
|
|
290
|
+
? stored.data.length
|
|
291
|
+
: JSON.stringify(stored.data).length,
|
|
292
|
+
get: (name) => headersMap.get(name.toLowerCase()) ?? null,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
recordOperation(type, key, value, options) {
|
|
296
|
+
this._operations.push({
|
|
297
|
+
type,
|
|
298
|
+
key,
|
|
299
|
+
value,
|
|
300
|
+
options,
|
|
301
|
+
timestamp: Date.now(),
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
checkErrorInjection(key, operation) {
|
|
305
|
+
for (let i = 0; i < this._errorInjections.length; i++) {
|
|
306
|
+
const injection = this._errorInjections[i];
|
|
307
|
+
// Check operation type
|
|
308
|
+
if (injection.operations && !injection.operations.includes(operation)) {
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
// Check pattern match
|
|
312
|
+
const matches = typeof injection.pattern === "string"
|
|
313
|
+
? key === injection.pattern || key.startsWith(injection.pattern + "/")
|
|
314
|
+
: injection.pattern.test(key);
|
|
315
|
+
if (matches) {
|
|
316
|
+
// Decrement count if specified
|
|
317
|
+
if (injection.count !== undefined) {
|
|
318
|
+
injection.count--;
|
|
319
|
+
if (injection.count <= 0) {
|
|
320
|
+
this._errorInjections.splice(i, 1);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return serviceError(injection.code, injection.message, "kv");
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
async simulateLatency() {
|
|
329
|
+
if (this._latencyMs > 0) {
|
|
330
|
+
await new Promise((resolve) => setTimeout(resolve, this._latencyMs));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
MockKVService.serviceName = "kv";
|
|
335
|
+
//# sourceMappingURL=MockKVService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockKVService.js","sourceRoot":"","sources":["../src/MockKVService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MockServiceContext - Test implementation of IServiceContext
|
|
3
|
+
*
|
|
4
|
+
* Provides a configurable mock context for testing services
|
|
5
|
+
* without network dependencies.
|
|
6
|
+
*/
|
|
7
|
+
import type { IServiceContext, IService, ServiceSession, RetryPolicy, InvokeFunction, FetchFunction, FetchRequestInit, EventHandler } from "@tinycloudlabs/sdk-services";
|
|
8
|
+
/**
|
|
9
|
+
* Recorded event with timestamp for assertion.
|
|
10
|
+
*/
|
|
11
|
+
export interface RecordedEvent {
|
|
12
|
+
event: string;
|
|
13
|
+
data: unknown;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Mock fetch response configuration.
|
|
18
|
+
*/
|
|
19
|
+
export interface MockFetchResponse {
|
|
20
|
+
ok: boolean;
|
|
21
|
+
status: number;
|
|
22
|
+
statusText: string;
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
body?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Mock fetch handler type.
|
|
28
|
+
* Can return a response directly or a function for dynamic responses.
|
|
29
|
+
*/
|
|
30
|
+
export type MockFetchHandler = MockFetchResponse | ((url: string, init?: FetchRequestInit) => MockFetchResponse | Promise<MockFetchResponse>);
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for MockServiceContext.
|
|
33
|
+
*/
|
|
34
|
+
export interface MockServiceContextConfig {
|
|
35
|
+
/** Initial session state */
|
|
36
|
+
session?: ServiceSession | null;
|
|
37
|
+
/** List of host URLs */
|
|
38
|
+
hosts?: string[];
|
|
39
|
+
/** Custom invoke function */
|
|
40
|
+
invoke?: InvokeFunction;
|
|
41
|
+
/** Default mock fetch responses by URL pattern */
|
|
42
|
+
fetchResponses?: Map<string | RegExp, MockFetchHandler>;
|
|
43
|
+
/** Default response for unmatched URLs */
|
|
44
|
+
defaultFetchResponse?: MockFetchResponse;
|
|
45
|
+
/** Retry policy configuration */
|
|
46
|
+
retryPolicy?: RetryPolicy;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* MockServiceContext implements IServiceContext for testing.
|
|
50
|
+
*
|
|
51
|
+
* Features:
|
|
52
|
+
* - In-memory service registry
|
|
53
|
+
* - Configurable mock invoke that returns test data
|
|
54
|
+
* - Configurable mock fetch with pattern matching
|
|
55
|
+
* - Event emission tracking for assertions
|
|
56
|
+
* - Session state management
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* const context = new MockServiceContext({
|
|
61
|
+
* session: createMockSession(),
|
|
62
|
+
* fetchResponses: new Map([
|
|
63
|
+
* ['/invoke', { ok: true, status: 200, body: { data: 'test' } }],
|
|
64
|
+
* ]),
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* const kv = new KVService({});
|
|
68
|
+
* kv.initialize(context);
|
|
69
|
+
* context.registerService('kv', kv);
|
|
70
|
+
*
|
|
71
|
+
* // Assert events were emitted
|
|
72
|
+
* expect(context.getEmittedEvents('service.request')).toHaveLength(1);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare class MockServiceContext implements IServiceContext {
|
|
76
|
+
private _session;
|
|
77
|
+
private _services;
|
|
78
|
+
private _eventHandlers;
|
|
79
|
+
private _emittedEvents;
|
|
80
|
+
private _abortController;
|
|
81
|
+
private _fetchResponses;
|
|
82
|
+
private _defaultFetchResponse;
|
|
83
|
+
private _invokeCallCount;
|
|
84
|
+
private _fetchCallCount;
|
|
85
|
+
private _invokeFunction;
|
|
86
|
+
readonly hosts: string[];
|
|
87
|
+
readonly retryPolicy: RetryPolicy;
|
|
88
|
+
constructor(config?: MockServiceContextConfig);
|
|
89
|
+
get session(): ServiceSession | null;
|
|
90
|
+
get isAuthenticated(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* Set the session (test helper).
|
|
93
|
+
*/
|
|
94
|
+
setSession(session: ServiceSession | null): void;
|
|
95
|
+
/**
|
|
96
|
+
* Default invoke implementation that returns mock headers.
|
|
97
|
+
*/
|
|
98
|
+
private defaultInvoke;
|
|
99
|
+
get invoke(): InvokeFunction;
|
|
100
|
+
get fetch(): FetchFunction;
|
|
101
|
+
registerService(name: string, service: IService): void;
|
|
102
|
+
unregisterService(name: string): void;
|
|
103
|
+
getService<T extends IService>(name: string): T | undefined;
|
|
104
|
+
emit(event: string, data: unknown): void;
|
|
105
|
+
on(event: string, handler: EventHandler): () => void;
|
|
106
|
+
get abortSignal(): AbortSignal;
|
|
107
|
+
/**
|
|
108
|
+
* Abort all operations (test helper).
|
|
109
|
+
*/
|
|
110
|
+
abort(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Get all emitted events.
|
|
113
|
+
*/
|
|
114
|
+
getEmittedEvents(): RecordedEvent[];
|
|
115
|
+
/**
|
|
116
|
+
* Get emitted events filtered by event name.
|
|
117
|
+
*/
|
|
118
|
+
getEmittedEventsByName(eventName: string): RecordedEvent[];
|
|
119
|
+
/**
|
|
120
|
+
* Check if a specific event was emitted.
|
|
121
|
+
*/
|
|
122
|
+
wasEventEmitted(eventName: string): boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Get the count of events by name.
|
|
125
|
+
*/
|
|
126
|
+
getEventCount(eventName: string): number;
|
|
127
|
+
/**
|
|
128
|
+
* Clear all recorded events.
|
|
129
|
+
*/
|
|
130
|
+
clearEmittedEvents(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Get the number of invoke calls.
|
|
133
|
+
*/
|
|
134
|
+
get invokeCallCount(): number;
|
|
135
|
+
/**
|
|
136
|
+
* Get the number of fetch calls.
|
|
137
|
+
*/
|
|
138
|
+
get fetchCallCount(): number;
|
|
139
|
+
/**
|
|
140
|
+
* Reset call counts.
|
|
141
|
+
*/
|
|
142
|
+
resetCallCounts(): void;
|
|
143
|
+
/**
|
|
144
|
+
* Add a mock fetch response for a URL pattern.
|
|
145
|
+
*/
|
|
146
|
+
addFetchResponse(pattern: string | RegExp, response: MockFetchHandler): void;
|
|
147
|
+
/**
|
|
148
|
+
* Remove a mock fetch response.
|
|
149
|
+
*/
|
|
150
|
+
removeFetchResponse(pattern: string | RegExp): void;
|
|
151
|
+
/**
|
|
152
|
+
* Clear all mock fetch responses.
|
|
153
|
+
*/
|
|
154
|
+
clearFetchResponses(): void;
|
|
155
|
+
/**
|
|
156
|
+
* Set the default fetch response.
|
|
157
|
+
*/
|
|
158
|
+
setDefaultFetchResponse(response: MockFetchResponse): void;
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=MockServiceContext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MockServiceContext.d.ts","sourceRoot":"","sources":["../src/MockServiceContext.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,QAAQ,EACR,cAAc,EACd,WAAW,EACX,cAAc,EACd,aAAa,EAEb,gBAAgB,EAChB,YAAY,EAEb,MAAM,6BAA6B,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GACxB,iBAAiB,GACjB,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,gBAAgB,KAAK,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;AAE/F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,4BAA4B;IAC5B,OAAO,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAChC,wBAAwB;IACxB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,6BAA6B;IAC7B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,kDAAkD;IAClD,cAAc,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,gBAAgB,CAAC,CAAC;IACxD,0CAA0C;IAC1C,oBAAoB,CAAC,EAAE,iBAAiB,CAAC;IACzC,iCAAiC;IACjC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,gBAAgB,CAA0C;IAClE,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,qBAAqB,CAAoB;IACjD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,eAAe,CAAiB;IAExC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;gBAEtB,MAAM,GAAE,wBAA6B;IAwBjD,IAAI,OAAO,IAAI,cAAc,GAAG,IAAI,CAEnC;IAED,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,GAAG,IAAI;IAahD;;OAEG;IACH,OAAO,CAAC,aAAa;IAcrB,IAAI,MAAM,IAAI,cAAc,CAU3B;IAED,IAAI,KAAK,IAAI,aAAa,CA4CzB;IAMD,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAG,IAAI;IAItD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIrC,UAAU,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAQ3D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAqBxC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,IAAI;IAqBpD,IAAI,WAAW,IAAI,WAAW,CAE7B;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;IAab;;OAEG;IACH,gBAAgB,IAAI,aAAa,EAAE;IAInC;;OAEG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE;IAI1D;;OAEG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIxC;;OAEG;IACH,kBAAkB,IAAI,IAAI;IAI1B;;OAEG;IACH,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED;;OAEG;IACH,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED;;OAEG;IACH,eAAe,IAAI,IAAI;IAKvB;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI;IAI5E;;OAEG;IACH,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAInD;;OAEG;IACH,mBAAmB,IAAI,IAAI;IAI3B;;OAEG;IACH,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;CAG3D"}
|