@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.
- package/LICENSE +33 -0
- package/README.md +400 -0
- package/dist/src/builders/index.d.ts +7 -0
- package/dist/src/builders/index.d.ts.map +1 -0
- package/dist/src/builders/index.js +6 -0
- package/dist/src/builders/leader-child-builder.d.ts +50 -0
- package/dist/src/builders/leader-child-builder.d.ts.map +1 -0
- package/dist/src/builders/leader-child-builder.js +59 -0
- package/dist/src/builders/manager-worker-builder.d.ts +49 -0
- package/dist/src/builders/manager-worker-builder.d.ts.map +1 -0
- package/dist/src/builders/manager-worker-builder.js +79 -0
- package/dist/src/builders/simple-node-builder.d.ts +49 -0
- package/dist/src/builders/simple-node-builder.d.ts.map +1 -0
- package/dist/src/builders/simple-node-builder.js +66 -0
- package/dist/src/example-tool.tool.d.ts +58 -0
- package/dist/src/example-tool.tool.d.ts.map +1 -0
- package/dist/src/example-tool.tool.js +89 -0
- package/dist/src/fixtures/index.d.ts +6 -0
- package/dist/src/fixtures/index.d.ts.map +1 -0
- package/dist/src/fixtures/index.js +5 -0
- package/dist/src/fixtures/mock-data.d.ts +201 -0
- package/dist/src/fixtures/mock-data.d.ts.map +1 -0
- package/dist/src/fixtures/mock-data.js +180 -0
- package/dist/src/fixtures/test-methods.d.ts +33 -0
- package/dist/src/fixtures/test-methods.d.ts.map +1 -0
- package/dist/src/fixtures/test-methods.js +185 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +25 -0
- package/dist/src/methods/example.methods.d.ts +9 -0
- package/dist/src/methods/example.methods.d.ts.map +1 -0
- package/dist/src/methods/example.methods.js +50 -0
- package/dist/src/test-environment.d.ts +185 -0
- package/dist/src/test-environment.d.ts.map +1 -0
- package/dist/src/test-environment.js +260 -0
- package/dist/src/utils/assertions.d.ts +159 -0
- package/dist/src/utils/assertions.d.ts.map +1 -0
- package/dist/src/utils/assertions.js +201 -0
- package/dist/src/utils/chunk-capture.d.ts +108 -0
- package/dist/src/utils/chunk-capture.d.ts.map +1 -0
- package/dist/src/utils/chunk-capture.js +156 -0
- package/dist/src/utils/index.d.ts +8 -0
- package/dist/src/utils/index.d.ts.map +1 -0
- package/dist/src/utils/index.js +7 -0
- package/dist/src/utils/mock-factories.d.ts +211 -0
- package/dist/src/utils/mock-factories.d.ts.map +1 -0
- package/dist/src/utils/mock-factories.js +181 -0
- package/dist/src/utils/wait-for.d.ts +42 -0
- package/dist/src/utils/wait-for.d.ts.map +1 -0
- package/dist/src/utils/wait-for.js +59 -0
- package/dist/test/example.spec.d.ts +1 -0
- package/dist/test/example.spec.d.ts.map +1 -0
- package/dist/test/example.spec.js +240 -0
- package/dist/test/fixtures/mock-data.d.ts +1 -0
- package/dist/test/fixtures/mock-data.d.ts.map +1 -0
- package/dist/test/fixtures/mock-data.js +90 -0
- package/dist/test/test-environment.spec.d.ts +8 -0
- package/dist/test/test-environment.spec.d.ts.map +1 -0
- package/dist/test/test-environment.spec.js +393 -0
- package/package.json +87 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Dual License: MIT OR Apache-2.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Olane Inc.
|
|
4
|
+
|
|
5
|
+
Olane OS is dual-licensed under your choice of either:
|
|
6
|
+
|
|
7
|
+
- **MIT License** (LICENSE-MIT or https://opensource.org/licenses/MIT)
|
|
8
|
+
- **Apache License, Version 2.0** (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
|
|
9
|
+
|
|
10
|
+
## Why Dual Licensing?
|
|
11
|
+
|
|
12
|
+
Dual licensing provides developers with flexibility when integrating Olane OS into their projects:
|
|
13
|
+
|
|
14
|
+
- **Choose MIT**: If you prefer a simple, permissive license without patent provisions
|
|
15
|
+
- **Choose Apache 2.0**: If you desire explicit patent protection and other features of the Apache license
|
|
16
|
+
|
|
17
|
+
You may choose either license to govern your use of this software.
|
|
18
|
+
|
|
19
|
+
## License Terms
|
|
20
|
+
|
|
21
|
+
### MIT License
|
|
22
|
+
The MIT license is very permissive and allows users to do almost anything with the software, including using, copying, modifying, merging, publishing, distributing, and selling it. The primary requirement is that the original copyright and license notice must be included in all copies of the software.
|
|
23
|
+
|
|
24
|
+
See LICENSE-MIT for the full license text.
|
|
25
|
+
|
|
26
|
+
### Apache License 2.0
|
|
27
|
+
The Apache License 2.0 is also a permissive free software license. It grants users the right to use, modify, and distribute the software. It also includes provisions regarding patent grants, which are not present in the MIT license.
|
|
28
|
+
|
|
29
|
+
See LICENSE-APACHE for the full license text.
|
|
30
|
+
|
|
31
|
+
## Contributing
|
|
32
|
+
|
|
33
|
+
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual-licensed as above, without any additional terms or conditions.
|
package/README.md
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# @olane/o-test
|
|
2
|
+
|
|
3
|
+
> Testing utilities and best practices for O-Network node development
|
|
4
|
+
|
|
5
|
+
## ⚠️ Important: We Use Mocha, Not Jest
|
|
6
|
+
|
|
7
|
+
Since O-Network is built on the **libp2p ecosystem**, we use **aegir** with **Mocha** for testing (NOT Jest).
|
|
8
|
+
|
|
9
|
+
- ✅ Use: `aegir`, `chai`, Mocha syntax (`before`, `after`, `.to.equal()`)
|
|
10
|
+
- ❌ Don't use: `jest`, `@types/jest`, `ts-jest`
|
|
11
|
+
|
|
12
|
+
See [MOCHA-MIGRATION.md](./MOCHA-MIGRATION.md) for migration guide.
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
`@olane/o-test` provides comprehensive testing guidelines, utilities, and examples for building reliable O-Network nodes in the Olane OS ecosystem.
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
### Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# From your package directory
|
|
24
|
+
pnpm install --save-dev @olane/o-test
|
|
25
|
+
|
|
26
|
+
# Install testing dependencies (aegir uses Mocha internally)
|
|
27
|
+
pnpm install --save-dev aegir chai
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Important:** Since we're using the **libp2p ecosystem**, we use **aegir** as our test runner, which uses **Mocha** (not Jest). Do not install Jest dependencies.
|
|
31
|
+
|
|
32
|
+
### Basic Test Structure
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import 'dotenv/config';
|
|
36
|
+
import { describe, it, before, after } from 'mocha';
|
|
37
|
+
import { expect } from 'chai';
|
|
38
|
+
import { oLeaderNode } from '@olane/o-leader';
|
|
39
|
+
import { MyTool } from '../src/my-tool.tool.js';
|
|
40
|
+
|
|
41
|
+
describe('MyTool', () => {
|
|
42
|
+
let leaderNode: oLeaderNode;
|
|
43
|
+
let tool: MyTool;
|
|
44
|
+
|
|
45
|
+
before(async () => {
|
|
46
|
+
// Create leader
|
|
47
|
+
leaderNode = new oLeaderNode({
|
|
48
|
+
parent: null,
|
|
49
|
+
leader: null,
|
|
50
|
+
});
|
|
51
|
+
await leaderNode.start();
|
|
52
|
+
|
|
53
|
+
// Create tool
|
|
54
|
+
tool = new MyTool({
|
|
55
|
+
parent: leaderNode.address,
|
|
56
|
+
leader: leaderNode.address,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Register with parent
|
|
60
|
+
(tool as any).hookInitializeFinished = () => {
|
|
61
|
+
leaderNode.addChildNode(tool);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
await tool.start();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
after(async () => {
|
|
68
|
+
await tool.stop();
|
|
69
|
+
await leaderNode.stop();
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should start successfully', () => {
|
|
73
|
+
expect(tool.state).to.equal(NodeState.RUNNING);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should execute method', async () => {
|
|
77
|
+
const result = await tool.useSelf({
|
|
78
|
+
method: 'my_method',
|
|
79
|
+
params: { test: 'value' },
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(result.success).to.be.true;
|
|
83
|
+
expect(result.result.data).to.exist;
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Documentation
|
|
89
|
+
|
|
90
|
+
### 📚 [Complete Testing Guide](./TESTING.md)
|
|
91
|
+
|
|
92
|
+
Comprehensive guide covering:
|
|
93
|
+
- Testing philosophy and baseline requirements
|
|
94
|
+
- Testing stack and configuration
|
|
95
|
+
- Lifecycle, method, and parent-child testing patterns
|
|
96
|
+
- Error handling and validation tests
|
|
97
|
+
- Test helpers and utilities
|
|
98
|
+
- CI/CD integration
|
|
99
|
+
- Common pitfalls and troubleshooting
|
|
100
|
+
|
|
101
|
+
### 🔄 [Jest to Mocha Migration Guide](./MOCHA-MIGRATION.md)
|
|
102
|
+
|
|
103
|
+
Quick reference for converting tests from Jest to Mocha:
|
|
104
|
+
- Side-by-side syntax comparison
|
|
105
|
+
- Complete code examples
|
|
106
|
+
- Common pitfalls and solutions
|
|
107
|
+
- Quick reference table
|
|
108
|
+
|
|
109
|
+
## Baseline Requirements
|
|
110
|
+
|
|
111
|
+
Every O-Network package **must** have:
|
|
112
|
+
|
|
113
|
+
| Requirement | File/Location |
|
|
114
|
+
|-------------|---------------|
|
|
115
|
+
| Test directory | `/test/` |
|
|
116
|
+
| Lifecycle test | `test/lifecycle.spec.ts` |
|
|
117
|
+
| Method tests | `test/methods.spec.ts` |
|
|
118
|
+
| Aegir config | `.aegir.js` (optional) |
|
|
119
|
+
| Test script | `"test": "aegir test"` in package.json |
|
|
120
|
+
|
|
121
|
+
**Note:** No Jest configuration needed - aegir uses Mocha internally for the libp2p ecosystem.
|
|
122
|
+
|
|
123
|
+
## Minimum Test Coverage
|
|
124
|
+
|
|
125
|
+
- All nodes must have lifecycle tests (start/stop)
|
|
126
|
+
- All public methods (`_tool_*`) must have happy path tests
|
|
127
|
+
- All required parameters must have validation tests
|
|
128
|
+
- Parent-child patterns must test registration and routing
|
|
129
|
+
- Critical error paths must be tested
|
|
130
|
+
|
|
131
|
+
## Running Tests
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# Run all tests
|
|
135
|
+
pnpm test
|
|
136
|
+
|
|
137
|
+
# Run in watch mode
|
|
138
|
+
pnpm test:watch
|
|
139
|
+
|
|
140
|
+
# Run specific test file
|
|
141
|
+
pnpm test test/lifecycle.spec.ts
|
|
142
|
+
|
|
143
|
+
# Run with coverage
|
|
144
|
+
pnpm test -- --coverage
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Configuration Files
|
|
148
|
+
|
|
149
|
+
### `.aegir.js` (Optional)
|
|
150
|
+
|
|
151
|
+
Aegir works out of the box with sensible defaults. Configuration is only needed for customization:
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
export default {
|
|
155
|
+
test: {
|
|
156
|
+
target: ['node'], // or ['browser'], or ['node', 'browser']
|
|
157
|
+
},
|
|
158
|
+
build: {
|
|
159
|
+
bundlesizeMax: '100KB',
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Important for libp2p ecosystem:**
|
|
165
|
+
- ✅ Aegir uses **Mocha** as the test runner (not Jest)
|
|
166
|
+
- ✅ Use Mocha syntax: `before`, `after`, `beforeEach`, `afterEach`
|
|
167
|
+
- ✅ Use Chai assertions: `expect().to.equal()`, `expect().to.exist`, etc.
|
|
168
|
+
- ❌ Do NOT install or use `jest`, `@types/jest`, or `ts-jest`
|
|
169
|
+
|
|
170
|
+
## Critical Testing Rules
|
|
171
|
+
|
|
172
|
+
### DO:
|
|
173
|
+
|
|
174
|
+
- Load environment with `import 'dotenv/config'`
|
|
175
|
+
- Use `.js` extensions in imports (ESM requirement)
|
|
176
|
+
- Create leader node before child nodes
|
|
177
|
+
- Inject `hookInitializeFinished` for parent-child registration
|
|
178
|
+
- Clean up all nodes in `afterEach`
|
|
179
|
+
- Access response data via `result.result.data`
|
|
180
|
+
- Test both success and error paths
|
|
181
|
+
|
|
182
|
+
### L DON'T:
|
|
183
|
+
|
|
184
|
+
- Override `start()` method (use hooks instead)
|
|
185
|
+
- Forget to call `stop()` on nodes
|
|
186
|
+
- Access `result.data` directly (use `result.result.data`)
|
|
187
|
+
- Use mocks for node instances (use real nodes)
|
|
188
|
+
- Share mutable state between tests
|
|
189
|
+
|
|
190
|
+
## Testing Philosophy
|
|
191
|
+
|
|
192
|
+
We prioritize **practical, integration-oriented tests** that validate real node behavior:
|
|
193
|
+
|
|
194
|
+
- Real node instances over mocks
|
|
195
|
+
- Actual lifecycle management
|
|
196
|
+
- Parent-child relationships
|
|
197
|
+
- Simple, focused test cases
|
|
198
|
+
- Integration over unit isolation
|
|
199
|
+
|
|
200
|
+
## Test Patterns
|
|
201
|
+
|
|
202
|
+
### Lifecycle Testing
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
it('should start and stop successfully', async () => {
|
|
206
|
+
const node = new MyTool({
|
|
207
|
+
parent: null,
|
|
208
|
+
leader: null,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
await node.start();
|
|
212
|
+
expect(node.state).to.equal(NodeState.RUNNING);
|
|
213
|
+
|
|
214
|
+
await node.stop();
|
|
215
|
+
expect(node.state).to.equal(NodeState.STOPPED);
|
|
216
|
+
});
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Method Testing
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
it('should validate required parameters', async () => {
|
|
223
|
+
const result = await tool.useSelf({
|
|
224
|
+
method: 'my_method',
|
|
225
|
+
params: {}, // Missing required params
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
expect(result.success).to.be.false;
|
|
229
|
+
expect(result.error).to.include('required');
|
|
230
|
+
});
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Parent-Child Testing
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
it('should create and route to child', async () => {
|
|
237
|
+
// Create child
|
|
238
|
+
const createResult = await manager.useSelf({
|
|
239
|
+
method: 'create_worker',
|
|
240
|
+
params: { workerId: 'worker-1' },
|
|
241
|
+
});
|
|
242
|
+
expect(createResult.success).to.be.true;
|
|
243
|
+
|
|
244
|
+
// Route to child
|
|
245
|
+
const routeResult = await manager.useSelf({
|
|
246
|
+
method: 'use_worker',
|
|
247
|
+
params: {
|
|
248
|
+
workerId: 'worker-1',
|
|
249
|
+
method: 'process_task',
|
|
250
|
+
params: { data: 'test' },
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
expect(routeResult.success).to.be.true;
|
|
254
|
+
});
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
## Common Pitfalls
|
|
258
|
+
|
|
259
|
+
### Pitfall 1: Missing Hook Injection
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// L WRONG
|
|
263
|
+
const tool = new MyTool({ parent: leader.address, leader: leader.address });
|
|
264
|
+
await tool.start(); // Child not registered!
|
|
265
|
+
|
|
266
|
+
// CORRECT
|
|
267
|
+
const tool = new MyTool({ parent: leader.address, leader: leader.address });
|
|
268
|
+
(tool as any).hookInitializeFinished = () => {
|
|
269
|
+
leaderNode.addChildNode(tool);
|
|
270
|
+
};
|
|
271
|
+
await tool.start();
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Pitfall 2: No Cleanup
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
// L WRONG
|
|
278
|
+
it('test', async () => {
|
|
279
|
+
const tool = new MyTool({});
|
|
280
|
+
await tool.start();
|
|
281
|
+
// No cleanup - nodes leak!
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// CORRECT
|
|
285
|
+
afterEach(async () => {
|
|
286
|
+
if (tool) await tool.stop();
|
|
287
|
+
if (leader) await leader.stop();
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Pitfall 3: Wrong Response Access
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// L WRONG
|
|
295
|
+
const data = result.data; // undefined!
|
|
296
|
+
|
|
297
|
+
// CORRECT
|
|
298
|
+
const data = result.result.data;
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Test Helpers
|
|
302
|
+
|
|
303
|
+
Create shared utilities for common patterns:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
// test/helpers/test-utils.ts
|
|
307
|
+
export async function createToolWithLeader<T>(
|
|
308
|
+
ToolClass: new (config: any) => T,
|
|
309
|
+
config: any = {}
|
|
310
|
+
): Promise<{ leader: oLeaderNode; tool: T }> {
|
|
311
|
+
const leader = new oLeaderNode({ parent: null, leader: null });
|
|
312
|
+
await leader.start();
|
|
313
|
+
|
|
314
|
+
const tool = new ToolClass({
|
|
315
|
+
...config,
|
|
316
|
+
parent: leader.address,
|
|
317
|
+
leader: leader.address,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
(tool as any).hookInitializeFinished = () => {
|
|
321
|
+
leader.addChildNode(tool as any);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
await (tool as any).start();
|
|
325
|
+
|
|
326
|
+
return { leader, tool };
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Example Tests
|
|
331
|
+
|
|
332
|
+
See the `test/` directory for complete examples:
|
|
333
|
+
|
|
334
|
+
- `test/lifecycle.spec.ts` - Node lifecycle testing
|
|
335
|
+
- `test/methods.spec.ts` - Method validation and execution
|
|
336
|
+
- `test/parent-child.spec.ts` - Manager/worker pattern testing
|
|
337
|
+
- `test/helpers/` - Shared test utilities
|
|
338
|
+
|
|
339
|
+
## Resources
|
|
340
|
+
|
|
341
|
+
- [TESTING.md](./TESTING.md) - Complete testing guide
|
|
342
|
+
- [CLAUDE.md](./CLAUDE.md) - O-Network node development guide
|
|
343
|
+
- [Olane Documentation](https://docs.olane.ai) - Full ecosystem docs
|
|
344
|
+
|
|
345
|
+
## Package Structure
|
|
346
|
+
|
|
347
|
+
```
|
|
348
|
+
o-test/
|
|
349
|
+
src/
|
|
350
|
+
index.ts # Public exports
|
|
351
|
+
example-tool.tool.ts # Example tool implementation
|
|
352
|
+
methods/
|
|
353
|
+
example.methods.ts # Method definitions
|
|
354
|
+
test/
|
|
355
|
+
lifecycle.spec.ts # Lifecycle tests
|
|
356
|
+
methods.spec.ts # Method tests
|
|
357
|
+
parent-child.spec.ts # Parent-child tests
|
|
358
|
+
helpers/
|
|
359
|
+
test-utils.ts # Test utilities
|
|
360
|
+
fixtures/
|
|
361
|
+
mock-data.ts # Test data
|
|
362
|
+
jest.config.js # Jest configuration
|
|
363
|
+
.aegir.js # Aegir configuration
|
|
364
|
+
tsconfig.json # TypeScript configuration
|
|
365
|
+
package.json # Package metadata
|
|
366
|
+
README.md # This file
|
|
367
|
+
TESTING.md # Complete testing guide
|
|
368
|
+
CLAUDE.md # Development guide
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Contributing
|
|
372
|
+
|
|
373
|
+
When adding tests:
|
|
374
|
+
|
|
375
|
+
1. Follow the patterns in [TESTING.md](./TESTING.md)
|
|
376
|
+
2. Ensure all baseline requirements are met
|
|
377
|
+
3. Test both success and error paths
|
|
378
|
+
4. Clean up all resources
|
|
379
|
+
5. Use descriptive test names
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
See the root LICENSE file in the Olane monorepo.
|
|
384
|
+
|
|
385
|
+
## Support
|
|
386
|
+
|
|
387
|
+
- GitHub Issues: [olane/issues](https://github.com/olane-labs/olane/issues)
|
|
388
|
+
- Documentation: [docs.olane.ai](https://docs.olane.ai)
|
|
389
|
+
- Community: [Discord](https://discord.gg/olane)
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
**Remember:**
|
|
394
|
+
- Real nodes, not mocks
|
|
395
|
+
- Proper lifecycle management
|
|
396
|
+
- Clean up in `afterEach`
|
|
397
|
+
- Test integration, not isolation
|
|
398
|
+
- Keep it simple
|
|
399
|
+
|
|
400
|
+
For detailed testing patterns and examples, see [TESTING.md](./TESTING.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test builders - Fluent APIs for common test scenarios
|
|
3
|
+
*/
|
|
4
|
+
export { SimpleNodeBuilder } from './simple-node-builder.js';
|
|
5
|
+
export { LeaderChildBuilder, type LeaderConfig } from './leader-child-builder.js';
|
|
6
|
+
export { ManagerWorkerBuilder, type ManagerWorkerResult } from './manager-worker-builder.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builders/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,KAAK,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,KAAK,mBAAmB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LeaderChildBuilder - Fluent API for creating leader-child hierarchies
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const { leader, tool } = await new LeaderChildBuilder(MyTool)
|
|
7
|
+
* .withToolConfig({ apiKey: 'test' })
|
|
8
|
+
* .withLeaderAddress('o://test-leader')
|
|
9
|
+
* .build(env);
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import type { oNode, oNodeAddress } from '@olane/o-node';
|
|
13
|
+
import type { TestEnvironment, TestNodeConfig, ToolWithLeaderResult } from '../test-environment.js';
|
|
14
|
+
export interface LeaderConfig {
|
|
15
|
+
address?: oNodeAddress;
|
|
16
|
+
description?: string;
|
|
17
|
+
autoStart?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class LeaderChildBuilder<T extends oNode = any> {
|
|
20
|
+
private toolClass;
|
|
21
|
+
private toolConfig;
|
|
22
|
+
private leaderClass?;
|
|
23
|
+
private leaderConfig;
|
|
24
|
+
constructor(ToolClass: new (config: any) => T);
|
|
25
|
+
/**
|
|
26
|
+
* Set tool configuration
|
|
27
|
+
*/
|
|
28
|
+
withToolConfig(config: TestNodeConfig): this;
|
|
29
|
+
/**
|
|
30
|
+
* Set leader class (optional, defaults to oLeaderNode)
|
|
31
|
+
*/
|
|
32
|
+
withLeaderClass(LeaderClass: new (config: any) => any): this;
|
|
33
|
+
/**
|
|
34
|
+
* Set leader configuration
|
|
35
|
+
*/
|
|
36
|
+
withLeaderConfig(config: LeaderConfig): this;
|
|
37
|
+
/**
|
|
38
|
+
* Set leader address
|
|
39
|
+
*/
|
|
40
|
+
withLeaderAddress(address: string | oNodeAddress): this;
|
|
41
|
+
/**
|
|
42
|
+
* Set leader description
|
|
43
|
+
*/
|
|
44
|
+
withLeaderDescription(description: string): this;
|
|
45
|
+
/**
|
|
46
|
+
* Build and create the leader-child hierarchy
|
|
47
|
+
*/
|
|
48
|
+
build(env: TestEnvironment): Promise<ToolWithLeaderResult<T>>;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=leader-child-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"leader-child-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/leader-child-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAEpG,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,qBAAa,kBAAkB,CAAC,CAAC,SAAS,KAAK,GAAG,GAAG;IACnD,OAAO,CAAC,SAAS,CAAyB;IAC1C,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,WAAW,CAAC,CAA2B;IAC/C,OAAO,CAAC,YAAY,CAAoB;gBAE5B,SAAS,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,CAAC;IAI7C;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAK5C;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,GAAG,GAAG,IAAI;IAK5D;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IAK5C;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAKvD;;OAEG;IACH,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKhD;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC;CAOpE"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LeaderChildBuilder - Fluent API for creating leader-child hierarchies
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const { leader, tool } = await new LeaderChildBuilder(MyTool)
|
|
7
|
+
* .withToolConfig({ apiKey: 'test' })
|
|
8
|
+
* .withLeaderAddress('o://test-leader')
|
|
9
|
+
* .build(env);
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export class LeaderChildBuilder {
|
|
13
|
+
constructor(ToolClass) {
|
|
14
|
+
this.toolConfig = {};
|
|
15
|
+
this.leaderConfig = {};
|
|
16
|
+
this.toolClass = ToolClass;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Set tool configuration
|
|
20
|
+
*/
|
|
21
|
+
withToolConfig(config) {
|
|
22
|
+
this.toolConfig = { ...this.toolConfig, ...config };
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Set leader class (optional, defaults to oLeaderNode)
|
|
27
|
+
*/
|
|
28
|
+
withLeaderClass(LeaderClass) {
|
|
29
|
+
this.leaderClass = LeaderClass;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Set leader configuration
|
|
34
|
+
*/
|
|
35
|
+
withLeaderConfig(config) {
|
|
36
|
+
this.leaderConfig = { ...this.leaderConfig, ...config };
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Set leader address
|
|
41
|
+
*/
|
|
42
|
+
withLeaderAddress(address) {
|
|
43
|
+
this.leaderConfig.address = address;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Set leader description
|
|
48
|
+
*/
|
|
49
|
+
withLeaderDescription(description) {
|
|
50
|
+
this.leaderConfig.description = description;
|
|
51
|
+
return this;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build and create the leader-child hierarchy
|
|
55
|
+
*/
|
|
56
|
+
async build(env) {
|
|
57
|
+
return await env.createToolWithLeader(this.toolClass, this.toolConfig, this.leaderClass);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ManagerWorkerBuilder - Fluent API for manager-worker pattern tests
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const { leader, manager, workers } = await new ManagerWorkerBuilder(MyManager, MyWorker)
|
|
7
|
+
* .withManagerConfig({ maxInstances: 5 })
|
|
8
|
+
* .withWorkerCount(3)
|
|
9
|
+
* .withWorkerConfig({ timeout: 5000 })
|
|
10
|
+
* .build(env);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
import type { oNode } from '@olane/o-node';
|
|
14
|
+
import type { TestEnvironment, TestNodeConfig } from '../test-environment.js';
|
|
15
|
+
export interface ManagerWorkerResult<M = any, W = any> {
|
|
16
|
+
leader: any;
|
|
17
|
+
manager: M;
|
|
18
|
+
workers: W[];
|
|
19
|
+
}
|
|
20
|
+
export declare class ManagerWorkerBuilder<M extends oNode = any, W extends oNode = any> {
|
|
21
|
+
private managerClass;
|
|
22
|
+
private workerClass?;
|
|
23
|
+
private managerConfig;
|
|
24
|
+
private workerConfig;
|
|
25
|
+
private workerCount;
|
|
26
|
+
private leaderClass?;
|
|
27
|
+
constructor(ManagerClass: new (config: any) => M, WorkerClass?: new (config: any) => W);
|
|
28
|
+
/**
|
|
29
|
+
* Set manager configuration
|
|
30
|
+
*/
|
|
31
|
+
withManagerConfig(config: TestNodeConfig): this;
|
|
32
|
+
/**
|
|
33
|
+
* Set worker configuration
|
|
34
|
+
*/
|
|
35
|
+
withWorkerConfig(config: TestNodeConfig): this;
|
|
36
|
+
/**
|
|
37
|
+
* Set number of workers to create
|
|
38
|
+
*/
|
|
39
|
+
withWorkerCount(count: number): this;
|
|
40
|
+
/**
|
|
41
|
+
* Set leader class
|
|
42
|
+
*/
|
|
43
|
+
withLeaderClass(LeaderClass: new (config: any) => any): this;
|
|
44
|
+
/**
|
|
45
|
+
* Build and create the manager-worker hierarchy
|
|
46
|
+
*/
|
|
47
|
+
build(env: TestEnvironment): Promise<ManagerWorkerResult<M, W>>;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=manager-worker-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager-worker-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/manager-worker-builder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE9E,MAAM,WAAW,mBAAmB,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG;IACnD,MAAM,EAAE,GAAG,CAAC;IACZ,OAAO,EAAE,CAAC,CAAC;IACX,OAAO,EAAE,CAAC,EAAE,CAAC;CACd;AAED,qBAAa,oBAAoB,CAAC,CAAC,SAAS,KAAK,GAAG,GAAG,EAAE,CAAC,SAAS,KAAK,GAAG,GAAG;IAC5E,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,WAAW,CAAC,CAAyB;IAC7C,OAAO,CAAC,aAAa,CAAsB;IAC3C,OAAO,CAAC,YAAY,CAAsB;IAC1C,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,WAAW,CAAC,CAA2B;gBAG7C,YAAY,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,CAAC,EACpC,WAAW,CAAC,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,CAAC;IAMtC;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAK/C;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAK9C;;OAEG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKpC;;OAEG;IACH,eAAe,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,GAAG,KAAK,GAAG,GAAG,IAAI;IAK5D;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;CAiCtE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ManagerWorkerBuilder - Fluent API for manager-worker pattern tests
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const { leader, manager, workers } = await new ManagerWorkerBuilder(MyManager, MyWorker)
|
|
7
|
+
* .withManagerConfig({ maxInstances: 5 })
|
|
8
|
+
* .withWorkerCount(3)
|
|
9
|
+
* .withWorkerConfig({ timeout: 5000 })
|
|
10
|
+
* .build(env);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export class ManagerWorkerBuilder {
|
|
14
|
+
constructor(ManagerClass, WorkerClass) {
|
|
15
|
+
this.managerConfig = {};
|
|
16
|
+
this.workerConfig = {};
|
|
17
|
+
this.workerCount = 0;
|
|
18
|
+
this.managerClass = ManagerClass;
|
|
19
|
+
this.workerClass = WorkerClass;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Set manager configuration
|
|
23
|
+
*/
|
|
24
|
+
withManagerConfig(config) {
|
|
25
|
+
this.managerConfig = { ...this.managerConfig, ...config };
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Set worker configuration
|
|
30
|
+
*/
|
|
31
|
+
withWorkerConfig(config) {
|
|
32
|
+
this.workerConfig = { ...this.workerConfig, ...config };
|
|
33
|
+
return this;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Set number of workers to create
|
|
37
|
+
*/
|
|
38
|
+
withWorkerCount(count) {
|
|
39
|
+
this.workerCount = count;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Set leader class
|
|
44
|
+
*/
|
|
45
|
+
withLeaderClass(LeaderClass) {
|
|
46
|
+
this.leaderClass = LeaderClass;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Build and create the manager-worker hierarchy
|
|
51
|
+
*/
|
|
52
|
+
async build(env) {
|
|
53
|
+
// Create leader and manager
|
|
54
|
+
const { leader, tool: manager } = await env.createToolWithLeader(this.managerClass, this.managerConfig, this.leaderClass);
|
|
55
|
+
const workers = [];
|
|
56
|
+
// Create workers if count specified and worker class provided
|
|
57
|
+
if (this.workerCount > 0 && this.workerClass) {
|
|
58
|
+
for (let i = 0; i < this.workerCount; i++) {
|
|
59
|
+
// Use manager's create_worker method if available
|
|
60
|
+
const createMethod = 'create_worker';
|
|
61
|
+
if (typeof manager.useSelf === 'function') {
|
|
62
|
+
try {
|
|
63
|
+
await manager.useSelf({
|
|
64
|
+
method: createMethod,
|
|
65
|
+
params: {
|
|
66
|
+
workerId: `worker-${i}`,
|
|
67
|
+
config: this.workerConfig,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.warn(`Could not create worker ${i} via manager method:`, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { leader, manager, workers };
|
|
78
|
+
}
|
|
79
|
+
}
|