@olane/o-test 0.7.12

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.
Files changed (60) hide show
  1. package/LICENSE +33 -0
  2. package/README.md +400 -0
  3. package/dist/src/builders/index.d.ts +7 -0
  4. package/dist/src/builders/index.d.ts.map +1 -0
  5. package/dist/src/builders/index.js +6 -0
  6. package/dist/src/builders/leader-child-builder.d.ts +50 -0
  7. package/dist/src/builders/leader-child-builder.d.ts.map +1 -0
  8. package/dist/src/builders/leader-child-builder.js +59 -0
  9. package/dist/src/builders/manager-worker-builder.d.ts +49 -0
  10. package/dist/src/builders/manager-worker-builder.d.ts.map +1 -0
  11. package/dist/src/builders/manager-worker-builder.js +79 -0
  12. package/dist/src/builders/simple-node-builder.d.ts +49 -0
  13. package/dist/src/builders/simple-node-builder.d.ts.map +1 -0
  14. package/dist/src/builders/simple-node-builder.js +66 -0
  15. package/dist/src/example-tool.tool.d.ts +58 -0
  16. package/dist/src/example-tool.tool.d.ts.map +1 -0
  17. package/dist/src/example-tool.tool.js +89 -0
  18. package/dist/src/fixtures/index.d.ts +6 -0
  19. package/dist/src/fixtures/index.d.ts.map +1 -0
  20. package/dist/src/fixtures/index.js +5 -0
  21. package/dist/src/fixtures/mock-data.d.ts +201 -0
  22. package/dist/src/fixtures/mock-data.d.ts.map +1 -0
  23. package/dist/src/fixtures/mock-data.js +180 -0
  24. package/dist/src/fixtures/test-methods.d.ts +33 -0
  25. package/dist/src/fixtures/test-methods.d.ts.map +1 -0
  26. package/dist/src/fixtures/test-methods.js +185 -0
  27. package/dist/src/index.d.ts +18 -0
  28. package/dist/src/index.d.ts.map +1 -0
  29. package/dist/src/index.js +25 -0
  30. package/dist/src/methods/example.methods.d.ts +9 -0
  31. package/dist/src/methods/example.methods.d.ts.map +1 -0
  32. package/dist/src/methods/example.methods.js +50 -0
  33. package/dist/src/test-environment.d.ts +185 -0
  34. package/dist/src/test-environment.d.ts.map +1 -0
  35. package/dist/src/test-environment.js +260 -0
  36. package/dist/src/utils/assertions.d.ts +159 -0
  37. package/dist/src/utils/assertions.d.ts.map +1 -0
  38. package/dist/src/utils/assertions.js +201 -0
  39. package/dist/src/utils/chunk-capture.d.ts +108 -0
  40. package/dist/src/utils/chunk-capture.d.ts.map +1 -0
  41. package/dist/src/utils/chunk-capture.js +156 -0
  42. package/dist/src/utils/index.d.ts +8 -0
  43. package/dist/src/utils/index.d.ts.map +1 -0
  44. package/dist/src/utils/index.js +7 -0
  45. package/dist/src/utils/mock-factories.d.ts +211 -0
  46. package/dist/src/utils/mock-factories.d.ts.map +1 -0
  47. package/dist/src/utils/mock-factories.js +181 -0
  48. package/dist/src/utils/wait-for.d.ts +42 -0
  49. package/dist/src/utils/wait-for.d.ts.map +1 -0
  50. package/dist/src/utils/wait-for.js +59 -0
  51. package/dist/test/example.spec.d.ts +1 -0
  52. package/dist/test/example.spec.d.ts.map +1 -0
  53. package/dist/test/example.spec.js +240 -0
  54. package/dist/test/fixtures/mock-data.d.ts +1 -0
  55. package/dist/test/fixtures/mock-data.d.ts.map +1 -0
  56. package/dist/test/fixtures/mock-data.js +90 -0
  57. package/dist/test/test-environment.spec.d.ts +8 -0
  58. package/dist/test/test-environment.spec.d.ts.map +1 -0
  59. package/dist/test/test-environment.spec.js +393 -0
  60. package/package.json +87 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * TestEnvironment - Core test setup and lifecycle management for O-Network nodes
3
+ *
4
+ * Provides automatic cleanup, node factories, and common test utilities
5
+ * to eliminate boilerplate in O-Network package tests.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * describe('MyTool', () => {
10
+ * const env = new TestEnvironment();
11
+ *
12
+ * afterEach(async () => {
13
+ * await env.cleanup();
14
+ * });
15
+ *
16
+ * it('should work', async () => {
17
+ * const { leader, tool } = await env.createToolWithLeader(MyTool);
18
+ * expect(tool.state).to.equal(NodeState.RUNNING);
19
+ * });
20
+ * });
21
+ * ```
22
+ */
23
+ import { NodeState } from '@olane/o-core';
24
+ /**
25
+ * Core test environment class for O-Network testing
26
+ *
27
+ * Manages node lifecycle, automatic cleanup, and provides
28
+ * factory methods for common test scenarios.
29
+ */
30
+ export class TestEnvironment {
31
+ constructor() {
32
+ this.nodes = [];
33
+ this.cleanupCallbacks = [];
34
+ }
35
+ /**
36
+ * Create a leader node for testing
37
+ *
38
+ * @param LeaderClass - Leader node class (e.g., oLeaderNode)
39
+ * @param options - Configuration options
40
+ * @returns Started leader node instance
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const leader = await env.createLeader(oLeaderNode);
45
+ * ```
46
+ */
47
+ async createLeader(LeaderClass, options = {}) {
48
+ const { autoStart = true, ...config } = options;
49
+ const leader = new LeaderClass({
50
+ parent: null,
51
+ leader: null,
52
+ ...config,
53
+ });
54
+ this.track(leader);
55
+ if (autoStart) {
56
+ await leader.start();
57
+ }
58
+ return leader;
59
+ }
60
+ /**
61
+ * Create a tool with a leader node
62
+ *
63
+ * Automatically handles:
64
+ * - Leader creation and startup
65
+ * - Tool creation with parent/leader references
66
+ * - Hook injection for child registration
67
+ * - Lifecycle tracking for cleanup
68
+ *
69
+ * @param ToolClass - Tool class to instantiate
70
+ * @param config - Tool configuration
71
+ * @param LeaderClass - Leader class (defaults to oLeaderNode if available)
72
+ * @returns Object with leader and tool instances
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const { leader, tool } = await env.createToolWithLeader(MyTool, {
77
+ * apiKey: 'test-key'
78
+ * });
79
+ * ```
80
+ */
81
+ async createToolWithLeader(ToolClass, config = {}, LeaderClass) {
82
+ // Dynamic import to avoid circular dependency
83
+ let leader;
84
+ if (LeaderClass) {
85
+ leader = await this.createLeader(LeaderClass);
86
+ }
87
+ else {
88
+ // Try to import oLeaderNode dynamically
89
+ try {
90
+ const { oLeaderNode } = await import('@olane/o-leader');
91
+ leader = await this.createLeader(oLeaderNode);
92
+ }
93
+ catch (error) {
94
+ throw new Error('LeaderClass not provided and @olane/o-leader not available. ' +
95
+ 'Please provide LeaderClass explicitly.');
96
+ }
97
+ }
98
+ const tool = new ToolClass({
99
+ ...config,
100
+ parent: leader.address,
101
+ leader: leader.address,
102
+ });
103
+ // Inject hook for parent-child registration
104
+ const originalHook = tool.hookInitializeFinished?.bind(tool);
105
+ tool.hookInitializeFinished = async () => {
106
+ leader.addChildNode(tool);
107
+ if (originalHook) {
108
+ await originalHook();
109
+ }
110
+ };
111
+ this.track(tool);
112
+ await tool.start();
113
+ return { leader, tool };
114
+ }
115
+ /**
116
+ * Create a simple node without leader
117
+ *
118
+ * @param NodeClass - Node class to instantiate
119
+ * @param config - Node configuration
120
+ * @param autoStart - Whether to start node automatically (default: true)
121
+ * @returns Node instance
122
+ *
123
+ * @example
124
+ * ```typescript
125
+ * const node = await env.createNode(MyTool, {
126
+ * address: new oNodeAddress('o://test')
127
+ * });
128
+ * ```
129
+ */
130
+ async createNode(NodeClass, config = {}, autoStart = true) {
131
+ const node = new NodeClass({
132
+ parent: null,
133
+ leader: null,
134
+ ...config,
135
+ });
136
+ this.track(node);
137
+ if (autoStart) {
138
+ await node.start();
139
+ }
140
+ return node;
141
+ }
142
+ /**
143
+ * Track a node for automatic cleanup
144
+ *
145
+ * @param node - Node instance to track
146
+ *
147
+ * @example
148
+ * ```typescript
149
+ * const node = new MyNode({});
150
+ * env.track(node);
151
+ * await node.start();
152
+ * ```
153
+ */
154
+ track(node) {
155
+ this.nodes.push(node);
156
+ }
157
+ /**
158
+ * Register a cleanup callback
159
+ *
160
+ * Useful for cleaning up external resources (databases, files, etc.)
161
+ *
162
+ * @param callback - Async cleanup function
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * const db = await createTestDB();
167
+ * env.onCleanup(async () => {
168
+ * await db.close();
169
+ * });
170
+ * ```
171
+ */
172
+ onCleanup(callback) {
173
+ this.cleanupCallbacks.push(callback);
174
+ }
175
+ /**
176
+ * Stop all tracked nodes and execute cleanup callbacks
177
+ *
178
+ * Stops nodes in reverse order (children before parents)
179
+ * Call this in afterEach hooks.
180
+ *
181
+ * @example
182
+ * ```typescript
183
+ * afterEach(async () => {
184
+ * await env.cleanup();
185
+ * });
186
+ * ```
187
+ */
188
+ async cleanup() {
189
+ // Stop nodes in reverse order (children first)
190
+ const nodesToStop = [...this.nodes].reverse();
191
+ for (const node of nodesToStop) {
192
+ try {
193
+ if (node.state === NodeState.RUNNING) {
194
+ await node.stop();
195
+ }
196
+ }
197
+ catch (error) {
198
+ console.error('Error stopping node:', error);
199
+ }
200
+ }
201
+ // Execute cleanup callbacks
202
+ for (const callback of this.cleanupCallbacks) {
203
+ try {
204
+ await callback();
205
+ }
206
+ catch (error) {
207
+ console.error('Error in cleanup callback:', error);
208
+ }
209
+ }
210
+ // Clear tracking
211
+ this.nodes = [];
212
+ this.cleanupCallbacks = [];
213
+ }
214
+ /**
215
+ * Get all tracked nodes
216
+ *
217
+ * @returns Array of tracked node instances
218
+ */
219
+ getNodes() {
220
+ return [...this.nodes];
221
+ }
222
+ /**
223
+ * Get count of tracked nodes
224
+ *
225
+ * @returns Number of tracked nodes
226
+ */
227
+ getNodeCount() {
228
+ return this.nodes.length;
229
+ }
230
+ /**
231
+ * Check if all tracked nodes are stopped
232
+ *
233
+ * @returns True if all nodes are stopped
234
+ */
235
+ allNodesStopped() {
236
+ return this.nodes.every(node => node.state === NodeState.STOPPED);
237
+ }
238
+ /**
239
+ * Wait for a condition to be true
240
+ *
241
+ * @param condition - Function that returns true when condition is met
242
+ * @param timeoutMs - Maximum time to wait in milliseconds (default: 5000)
243
+ * @param intervalMs - Check interval in milliseconds (default: 100)
244
+ * @returns Promise that resolves when condition is met or rejects on timeout
245
+ *
246
+ * @example
247
+ * ```typescript
248
+ * await env.waitFor(() => tool.isReady, 10000);
249
+ * ```
250
+ */
251
+ async waitFor(condition, timeoutMs = 5000, intervalMs = 100) {
252
+ const startTime = Date.now();
253
+ while (!condition()) {
254
+ if (Date.now() - startTime > timeoutMs) {
255
+ throw new Error(`Timeout waiting for condition after ${timeoutMs}ms`);
256
+ }
257
+ await new Promise(resolve => setTimeout(resolve, intervalMs));
258
+ }
259
+ }
260
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Assertion helpers for O-Network testing
3
+ *
4
+ * Provides type-safe, expressive assertions for common test scenarios
5
+ */
6
+ import { oResponse } from '@olane/o-core';
7
+ import type { oNode } from '@olane/o-node';
8
+ /**
9
+ * Assert that a tool response indicates success
10
+ *
11
+ * @param response - Tool method response (oResponse)
12
+ * @param message - Optional custom error message
13
+ * @throws Error if response is not successful
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const response = await tool.use(address, { method: 'test', params: {} });
18
+ * assertSuccess(response);
19
+ * ```
20
+ */
21
+ export declare function assertSuccess(response: oResponse, message?: string): asserts response is oResponse & {
22
+ result: {
23
+ success: true;
24
+ };
25
+ };
26
+ /**
27
+ * Assert that a tool response indicates failure
28
+ *
29
+ * @param response - Tool method response (oResponse)
30
+ * @param expectedError - Optional substring to match in error message
31
+ * @throws Error if response is successful or error doesn't match
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const response = await tool.use(address, { method: 'test', params: {} });
36
+ * assertError(response, 'required');
37
+ * ```
38
+ */
39
+ export declare function assertError(response: oResponse, expectedError?: string): asserts response is oResponse & {
40
+ result: {
41
+ success: false;
42
+ };
43
+ };
44
+ /**
45
+ * Assert that a node is in running state
46
+ *
47
+ * @param node - Node instance
48
+ * @param message - Optional custom error message
49
+ * @throws Error if node is not running
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * assertRunning(tool);
54
+ * ```
55
+ */
56
+ export declare function assertRunning(node: oNode, message?: string): void;
57
+ /**
58
+ * Assert that a node is in stopped state
59
+ *
60
+ * @param node - Node instance
61
+ * @param message - Optional custom error message
62
+ * @throws Error if node is not stopped
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * await tool.stop();
67
+ * assertStopped(tool);
68
+ * ```
69
+ */
70
+ export declare function assertStopped(node: oNode, message?: string): void;
71
+ /**
72
+ * Assert that response has data
73
+ *
74
+ * @param response - Tool method response (oResponse)
75
+ * @param message - Optional custom error message
76
+ * @throws Error if response doesn't have data
77
+ *
78
+ * @example
79
+ * ```typescript
80
+ * const response = await tool.use(address, { method: 'get_data', params: {} });
81
+ * assertHasData(response);
82
+ * const data = response.result.data;
83
+ * ```
84
+ */
85
+ export declare function assertHasData(response: oResponse, message?: string): asserts response is oResponse & {
86
+ result: {
87
+ data: any;
88
+ };
89
+ };
90
+ /**
91
+ * Assert that a value is defined (not null or undefined)
92
+ *
93
+ * @param value - Value to check
94
+ * @param name - Name of the value for error message
95
+ * @throws Error if value is null or undefined
96
+ *
97
+ * @example
98
+ * ```typescript
99
+ * assertDefined(user, 'user');
100
+ * // Now TypeScript knows user is not null/undefined
101
+ * ```
102
+ */
103
+ export declare function assertDefined<T>(value: T, name?: string): asserts value is NonNullable<T>;
104
+ /**
105
+ * Assert that an array has expected length
106
+ *
107
+ * @param array - Array to check
108
+ * @param expectedLength - Expected length
109
+ * @param message - Optional custom error message
110
+ * @throws Error if length doesn't match
111
+ *
112
+ * @example
113
+ * ```typescript
114
+ * assertArrayLength(items, 5);
115
+ * ```
116
+ */
117
+ export declare function assertArrayLength(array: any[], expectedLength: number, message?: string): void;
118
+ /**
119
+ * Assert that an object has a property
120
+ *
121
+ * @param obj - Object to check
122
+ * @param property - Property name
123
+ * @param message - Optional custom error message
124
+ * @throws Error if property doesn't exist
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * assertHasProperty(response.result.data, 'userId');
129
+ * ```
130
+ */
131
+ export declare function assertHasProperty<T extends object, K extends PropertyKey>(obj: T, property: K, message?: string): asserts obj is T & Record<K, unknown>;
132
+ /**
133
+ * Assert that a value matches one of the expected values
134
+ *
135
+ * @param value - Value to check
136
+ * @param expectedValues - Array of expected values
137
+ * @param message - Optional custom error message
138
+ * @throws Error if value doesn't match any expected value
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * assertOneOf(status, ['pending', 'active', 'completed']);
143
+ * ```
144
+ */
145
+ export declare function assertOneOf<T>(value: T, expectedValues: T[], message?: string): void;
146
+ /**
147
+ * Create a custom assertion
148
+ *
149
+ * @param condition - Condition to check
150
+ * @param message - Error message if condition is false
151
+ * @throws Error if condition is false
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * assert(user.age >= 18, 'User must be 18 or older');
156
+ * ```
157
+ */
158
+ export declare function assert(condition: boolean, message: string): asserts condition;
159
+ //# sourceMappingURL=assertions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assertions.d.ts","sourceRoot":"","sources":["../../../src/utils/assertions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAa,SAAS,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,IAAI,SAAS,GAAG;IAAE,MAAM,EAAE;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAA;CAAE,CAK/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,SAAS,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,QAAQ,IAAI,SAAS,GAAG;IAAE,MAAM,EAAE;QAAE,OAAO,EAAE,KAAK,CAAA;KAAE,CAAA;CAAE,CAchE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAKN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAKN;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,SAAS,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,IAAI,SAAS,GAAG;IAAE,MAAM,EAAE;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAA;CAAE,CAK3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAC7B,KAAK,EAAE,CAAC,EACR,IAAI,GAAE,MAAgB,GACrB,OAAO,CAAC,KAAK,IAAI,WAAW,CAAC,CAAC,CAAC,CAIjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,GAAG,EAAE,EACZ,cAAc,EAAE,MAAM,EACtB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAMN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,WAAW,EACvE,GAAG,EAAE,CAAC,EACN,QAAQ,EAAE,CAAC,EACX,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,CAMvC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,CAAC,EACR,cAAc,EAAE,CAAC,EAAE,EACnB,OAAO,CAAC,EAAE,MAAM,GACf,IAAI,CAMN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CACpB,SAAS,EAAE,OAAO,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,SAAS,CAInB"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Assertion helpers for O-Network testing
3
+ *
4
+ * Provides type-safe, expressive assertions for common test scenarios
5
+ */
6
+ import { NodeState } from '@olane/o-core';
7
+ /**
8
+ * Assert that a tool response indicates success
9
+ *
10
+ * @param response - Tool method response (oResponse)
11
+ * @param message - Optional custom error message
12
+ * @throws Error if response is not successful
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const response = await tool.use(address, { method: 'test', params: {} });
17
+ * assertSuccess(response);
18
+ * ```
19
+ */
20
+ export function assertSuccess(response, message) {
21
+ if (!response.result.success) {
22
+ const errorMsg = message || `Expected success but got error: ${response.result.error}`;
23
+ throw new Error(errorMsg);
24
+ }
25
+ }
26
+ /**
27
+ * Assert that a tool response indicates failure
28
+ *
29
+ * @param response - Tool method response (oResponse)
30
+ * @param expectedError - Optional substring to match in error message
31
+ * @throws Error if response is successful or error doesn't match
32
+ *
33
+ * @example
34
+ * ```typescript
35
+ * const response = await tool.use(address, { method: 'test', params: {} });
36
+ * assertError(response, 'required');
37
+ * ```
38
+ */
39
+ export function assertError(response, expectedError) {
40
+ if (response.result.success) {
41
+ throw new Error('Expected error but got success');
42
+ }
43
+ const errorMessage = typeof response.result.error === 'string'
44
+ ? response.result.error
45
+ : response.result.error?.message || JSON.stringify(response.result.error);
46
+ if (expectedError && !errorMessage?.includes(expectedError)) {
47
+ throw new Error(`Expected error to include "${expectedError}" but got: ${errorMessage}`);
48
+ }
49
+ }
50
+ /**
51
+ * Assert that a node is in running state
52
+ *
53
+ * @param node - Node instance
54
+ * @param message - Optional custom error message
55
+ * @throws Error if node is not running
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * assertRunning(tool);
60
+ * ```
61
+ */
62
+ export function assertRunning(node, message) {
63
+ if (node.state !== NodeState.RUNNING) {
64
+ const errorMsg = message || `Expected node to be RUNNING but got ${node.state}`;
65
+ throw new Error(errorMsg);
66
+ }
67
+ }
68
+ /**
69
+ * Assert that a node is in stopped state
70
+ *
71
+ * @param node - Node instance
72
+ * @param message - Optional custom error message
73
+ * @throws Error if node is not stopped
74
+ *
75
+ * @example
76
+ * ```typescript
77
+ * await tool.stop();
78
+ * assertStopped(tool);
79
+ * ```
80
+ */
81
+ export function assertStopped(node, message) {
82
+ if (node.state !== NodeState.STOPPED) {
83
+ const errorMsg = message || `Expected node to be STOPPED but got ${node.state}`;
84
+ throw new Error(errorMsg);
85
+ }
86
+ }
87
+ /**
88
+ * Assert that response has data
89
+ *
90
+ * @param response - Tool method response (oResponse)
91
+ * @param message - Optional custom error message
92
+ * @throws Error if response doesn't have data
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const response = await tool.use(address, { method: 'get_data', params: {} });
97
+ * assertHasData(response);
98
+ * const data = response.result.data;
99
+ * ```
100
+ */
101
+ export function assertHasData(response, message) {
102
+ if (!response.result?.data) {
103
+ const errorMsg = message || 'Expected response to have result.data';
104
+ throw new Error(errorMsg);
105
+ }
106
+ }
107
+ /**
108
+ * Assert that a value is defined (not null or undefined)
109
+ *
110
+ * @param value - Value to check
111
+ * @param name - Name of the value for error message
112
+ * @throws Error if value is null or undefined
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * assertDefined(user, 'user');
117
+ * // Now TypeScript knows user is not null/undefined
118
+ * ```
119
+ */
120
+ export function assertDefined(value, name = 'value') {
121
+ if (value === null || value === undefined) {
122
+ throw new Error(`Expected ${name} to be defined but got ${value}`);
123
+ }
124
+ }
125
+ /**
126
+ * Assert that an array has expected length
127
+ *
128
+ * @param array - Array to check
129
+ * @param expectedLength - Expected length
130
+ * @param message - Optional custom error message
131
+ * @throws Error if length doesn't match
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * assertArrayLength(items, 5);
136
+ * ```
137
+ */
138
+ export function assertArrayLength(array, expectedLength, message) {
139
+ if (array.length !== expectedLength) {
140
+ const errorMsg = message ||
141
+ `Expected array length ${expectedLength} but got ${array.length}`;
142
+ throw new Error(errorMsg);
143
+ }
144
+ }
145
+ /**
146
+ * Assert that an object has a property
147
+ *
148
+ * @param obj - Object to check
149
+ * @param property - Property name
150
+ * @param message - Optional custom error message
151
+ * @throws Error if property doesn't exist
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * assertHasProperty(response.result.data, 'userId');
156
+ * ```
157
+ */
158
+ export function assertHasProperty(obj, property, message) {
159
+ if (!(property in obj)) {
160
+ const errorMsg = message ||
161
+ `Expected object to have property "${String(property)}"`;
162
+ throw new Error(errorMsg);
163
+ }
164
+ }
165
+ /**
166
+ * Assert that a value matches one of the expected values
167
+ *
168
+ * @param value - Value to check
169
+ * @param expectedValues - Array of expected values
170
+ * @param message - Optional custom error message
171
+ * @throws Error if value doesn't match any expected value
172
+ *
173
+ * @example
174
+ * ```typescript
175
+ * assertOneOf(status, ['pending', 'active', 'completed']);
176
+ * ```
177
+ */
178
+ export function assertOneOf(value, expectedValues, message) {
179
+ if (!expectedValues.includes(value)) {
180
+ const errorMsg = message ||
181
+ `Expected one of [${expectedValues.join(', ')}] but got ${value}`;
182
+ throw new Error(errorMsg);
183
+ }
184
+ }
185
+ /**
186
+ * Create a custom assertion
187
+ *
188
+ * @param condition - Condition to check
189
+ * @param message - Error message if condition is false
190
+ * @throws Error if condition is false
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * assert(user.age >= 18, 'User must be 18 or older');
195
+ * ```
196
+ */
197
+ export function assert(condition, message) {
198
+ if (!condition) {
199
+ throw new Error(message);
200
+ }
201
+ }