@elizaos/cli 1.3.0 → 1.3.2
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/README.md +1 -1
- package/dist/{chunk-2CUIHNPL.js → chunk-5GUS4CFO.js} +7 -2
- package/dist/{chunk-2ALAPQLV.js → chunk-E6XYTE3A.js} +296 -311
- package/dist/chunk-GXWWPFBO.js +39 -0
- package/dist/{chunk-I77ZRNYO.js → chunk-T2QDIXGU.js} +2 -2
- package/dist/commands/agent/actions/index.d.ts +5 -0
- package/dist/commands/agent/actions/index.js +2 -2
- package/dist/commands/agent/index.d.ts +2 -2
- package/dist/commands/agent/index.js +2 -2
- package/dist/commands/create/actions/index.js +3 -3
- package/dist/commands/create/index.js +4 -4
- package/dist/commands/shared/index.d.ts +11 -28
- package/dist/commands/shared/index.js +7 -3
- package/dist/index.js +541 -450
- package/dist/{registry-N626N4VG.js → registry-433S5F3Y.js} +2 -2
- package/dist/templates/plugin-quick-starter/.gitignore +66 -0
- package/dist/templates/plugin-quick-starter/.npmignore +5 -0
- package/dist/templates/plugin-quick-starter/package.json +11 -3
- package/dist/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
- package/dist/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
- package/dist/templates/plugin-quick-starter/src/plugin.ts +7 -13
- package/dist/templates/plugin-starter/.gitignore +66 -0
- package/dist/templates/plugin-starter/.npmignore +5 -0
- package/dist/templates/plugin-starter/README.md +1 -1
- package/dist/templates/plugin-starter/package.json +11 -3
- package/dist/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
- package/dist/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
- package/dist/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
- package/dist/templates/plugin-starter/src/plugin.ts +18 -22
- package/dist/templates/project-starter/.gitignore +57 -0
- package/dist/templates/project-starter/.npmignore +11 -0
- package/dist/templates/project-starter/README.md +1 -1
- package/dist/templates/project-starter/package.json +4 -4
- package/dist/templates/project-starter/src/__tests__/env.test.ts +3 -1
- package/dist/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
- package/dist/templates/project-starter/src/__tests__/integration.test.ts +1 -1
- package/dist/templates/project-starter/tsup.config.ts +2 -1
- package/dist/templates/project-tee-starter/.dockerignore +64 -14
- package/dist/templates/project-tee-starter/.gitignore +57 -0
- package/dist/templates/project-tee-starter/.npmignore +6 -0
- package/dist/templates/project-tee-starter/Dockerfile +9 -5
- package/dist/templates/project-tee-starter/GUIDE.md +103 -42
- package/dist/templates/project-tee-starter/README.md +39 -19
- package/dist/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
- package/dist/templates/project-tee-starter/__tests__/character.test.ts +19 -17
- package/dist/templates/project-tee-starter/__tests__/config.test.ts +10 -3
- package/dist/templates/project-tee-starter/__tests__/env.test.ts +2 -1
- package/dist/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
- package/dist/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
- package/dist/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
- package/dist/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
- package/dist/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
- package/dist/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
- package/dist/templates/project-tee-starter/docker-compose.yaml +5 -2
- package/dist/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
- package/dist/templates/project-tee-starter/package.json +34 -14
- package/dist/templates/project-tee-starter/postcss.config.js +3 -0
- package/dist/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
- package/dist/templates/project-tee-starter/scripts/test-all.sh +82 -0
- package/dist/templates/project-tee-starter/src/frontend/index.css +106 -0
- package/dist/templates/project-tee-starter/src/frontend/index.html +20 -0
- package/dist/templates/project-tee-starter/src/frontend/index.tsx +370 -0
- package/dist/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
- package/dist/templates/project-tee-starter/src/frontend/utils.ts +6 -0
- package/dist/templates/project-tee-starter/src/index.ts +6 -6
- package/dist/templates/project-tee-starter/src/plugin.ts +209 -59
- package/dist/templates/project-tee-starter/tailwind.config.js +62 -0
- package/dist/templates/project-tee-starter/tsconfig.build.json +2 -2
- package/dist/templates/project-tee-starter/tsconfig.json +8 -5
- package/dist/templates/project-tee-starter/tsup.config.ts +3 -2
- package/dist/templates/project-tee-starter/vite.config.ts +39 -0
- package/dist/url-utils-CKc_Ebt_.d.ts +35 -0
- package/dist/{utils-H66532NB.js → utils-DBLSDYBF.js} +2 -2
- package/package.json +12 -7
- package/templates/plugin-quick-starter/.gitignore +66 -0
- package/templates/plugin-quick-starter/.npmignore +5 -0
- package/templates/plugin-quick-starter/package.json +11 -3
- package/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
- package/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
- package/templates/plugin-quick-starter/src/plugin.ts +7 -13
- package/templates/plugin-starter/.gitignore +66 -0
- package/templates/plugin-starter/.npmignore +5 -0
- package/templates/plugin-starter/README.md +1 -1
- package/templates/plugin-starter/package.json +11 -3
- package/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
- package/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
- package/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
- package/templates/plugin-starter/src/plugin.ts +18 -22
- package/templates/project-starter/.gitignore +57 -0
- package/templates/project-starter/.npmignore +11 -0
- package/templates/project-starter/README.md +1 -1
- package/templates/project-starter/package.json +4 -4
- package/templates/project-starter/src/__tests__/env.test.ts +3 -1
- package/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
- package/templates/project-starter/src/__tests__/integration.test.ts +1 -1
- package/templates/project-starter/tsup.config.ts +2 -1
- package/templates/project-tee-starter/.dockerignore +64 -14
- package/templates/project-tee-starter/.gitignore +57 -0
- package/templates/project-tee-starter/.npmignore +6 -0
- package/templates/project-tee-starter/Dockerfile +9 -5
- package/templates/project-tee-starter/GUIDE.md +103 -42
- package/templates/project-tee-starter/README.md +39 -19
- package/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
- package/templates/project-tee-starter/__tests__/character.test.ts +19 -17
- package/templates/project-tee-starter/__tests__/config.test.ts +10 -3
- package/templates/project-tee-starter/__tests__/env.test.ts +2 -1
- package/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
- package/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
- package/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
- package/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
- package/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
- package/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
- package/templates/project-tee-starter/docker-compose.yaml +5 -2
- package/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
- package/templates/project-tee-starter/package.json +34 -14
- package/templates/project-tee-starter/postcss.config.js +3 -0
- package/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
- package/templates/project-tee-starter/scripts/test-all.sh +82 -0
- package/templates/project-tee-starter/src/frontend/index.css +106 -0
- package/templates/project-tee-starter/src/frontend/index.html +20 -0
- package/templates/project-tee-starter/src/frontend/index.tsx +370 -0
- package/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
- package/templates/project-tee-starter/src/frontend/utils.ts +6 -0
- package/templates/project-tee-starter/src/index.ts +6 -6
- package/templates/project-tee-starter/src/plugin.ts +209 -59
- package/templates/project-tee-starter/tailwind.config.js +62 -0
- package/templates/project-tee-starter/tsconfig.build.json +2 -2
- package/templates/project-tee-starter/tsconfig.json +8 -5
- package/templates/project-tee-starter/tsup.config.ts +3 -2
- package/templates/project-tee-starter/vite.config.ts +39 -0
- package/dist/chunk-4O6EZU37.js +0 -14
- package/dist/migration-guides/advanced-migration-guide.md +0 -459
- package/dist/migration-guides/completion-requirements.md +0 -379
- package/dist/migration-guides/integrated-migration-loop.md +0 -392
- package/dist/migration-guides/migration-guide.md +0 -712
- package/dist/migration-guides/prompt-and-generation-guide.md +0 -702
- package/dist/migration-guides/state-and-providers-guide.md +0 -544
- package/dist/migration-guides/testing-guide.md +0 -1021
- package/dist/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
- package/dist/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
- package/dist/templates/plugin-starter/dist/index.js +0 -387
- package/dist/templates/plugin-starter/dist/index.js.map +0 -1
- package/templates/plugin-starter/dist/.vite/manifest.json +0 -11
- package/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
- package/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
- package/templates/plugin-starter/dist/index.d.ts +0 -14
- package/templates/plugin-starter/dist/index.js +0 -387
- package/templates/plugin-starter/dist/index.js.map +0 -1
|
@@ -1,1021 +0,0 @@
|
|
|
1
|
-
# ElizaOS Plugin Testing Guide
|
|
2
|
-
|
|
3
|
-
This guide provides comprehensive instructions for writing tests for ElizaOS plugins using Bun's test runner.
|
|
4
|
-
|
|
5
|
-
## Table of Contents
|
|
6
|
-
|
|
7
|
-
1. [Test Environment Setup](#1-test-environment-setup)
|
|
8
|
-
2. [Creating Test Utilities](#2-creating-test-utilities)
|
|
9
|
-
3. [Testing Actions](#3-testing-actions)
|
|
10
|
-
4. [Testing Providers](#4-testing-providers)
|
|
11
|
-
5. [Testing Evaluators](#5-testing-evaluators)
|
|
12
|
-
6. [Testing Services](#6-testing-services)
|
|
13
|
-
7. [Testing Event Handlers](#7-testing-event-handlers)
|
|
14
|
-
8. [Advanced Testing Patterns](#8-advanced-testing-patterns)
|
|
15
|
-
9. [Best Practices](#9-best-practices)
|
|
16
|
-
10. [Running Tests](#10-running-tests)
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 1. Test Environment Setup
|
|
21
|
-
|
|
22
|
-
### Directory Structure
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
src/
|
|
26
|
-
__tests__/
|
|
27
|
-
test-utils.ts # Shared test utilities and mocks
|
|
28
|
-
index.test.ts # Main plugin tests
|
|
29
|
-
actions.test.ts # Action tests
|
|
30
|
-
providers.test.ts # Provider tests
|
|
31
|
-
evaluators.test.ts # Evaluator tests
|
|
32
|
-
services.test.ts # Service tests
|
|
33
|
-
actions/
|
|
34
|
-
providers/
|
|
35
|
-
evaluators/
|
|
36
|
-
services/
|
|
37
|
-
index.ts
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### Required Dependencies
|
|
41
|
-
|
|
42
|
-
```json
|
|
43
|
-
{
|
|
44
|
-
"devDependencies": {
|
|
45
|
-
"@types/bun": "latest",
|
|
46
|
-
"bun-types": "latest"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Base Test Imports
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
import { describe, expect, it, mock, beforeEach, afterEach, spyOn } from 'bun:test';
|
|
55
|
-
import {
|
|
56
|
-
type IAgentRuntime,
|
|
57
|
-
type Memory,
|
|
58
|
-
type State,
|
|
59
|
-
type HandlerCallback,
|
|
60
|
-
type Action,
|
|
61
|
-
type Provider,
|
|
62
|
-
type Evaluator,
|
|
63
|
-
ModelType,
|
|
64
|
-
logger,
|
|
65
|
-
} from '@elizaos/core';
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## 2. Creating Test Utilities
|
|
71
|
-
|
|
72
|
-
Create a comprehensive `test-utils.ts` file with reusable mock objects and helper functions:
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
import { mock } from 'bun:test';
|
|
76
|
-
import {
|
|
77
|
-
type IAgentRuntime,
|
|
78
|
-
type Memory,
|
|
79
|
-
type State,
|
|
80
|
-
type Character,
|
|
81
|
-
type UUID,
|
|
82
|
-
type Content,
|
|
83
|
-
type Room,
|
|
84
|
-
type Entity,
|
|
85
|
-
ChannelType,
|
|
86
|
-
} from '@elizaos/core';
|
|
87
|
-
|
|
88
|
-
// Mock Runtime Type
|
|
89
|
-
export type MockRuntime = Partial<IAgentRuntime> & {
|
|
90
|
-
agentId: UUID;
|
|
91
|
-
character: Character;
|
|
92
|
-
getSetting: ReturnType<typeof mock>;
|
|
93
|
-
useModel: ReturnType<typeof mock>;
|
|
94
|
-
composeState: ReturnType<typeof mock>;
|
|
95
|
-
createMemory: ReturnType<typeof mock>;
|
|
96
|
-
getMemories: ReturnType<typeof mock>;
|
|
97
|
-
searchMemories: ReturnType<typeof mock>;
|
|
98
|
-
updateMemory: ReturnType<typeof mock>;
|
|
99
|
-
getRoom: ReturnType<typeof mock>;
|
|
100
|
-
getParticipantUserState: ReturnType<typeof mock>;
|
|
101
|
-
setParticipantUserState: ReturnType<typeof mock>;
|
|
102
|
-
emitEvent: ReturnType<typeof mock>;
|
|
103
|
-
getTasks: ReturnType<typeof mock>;
|
|
104
|
-
providers: any[];
|
|
105
|
-
actions: any[];
|
|
106
|
-
evaluators: any[];
|
|
107
|
-
services: any[];
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
// Create Mock Runtime
|
|
111
|
-
export function createMockRuntime(overrides: Partial<MockRuntime> = {}): MockRuntime {
|
|
112
|
-
return {
|
|
113
|
-
agentId: 'test-agent-id' as UUID,
|
|
114
|
-
character: {
|
|
115
|
-
name: 'Test Agent',
|
|
116
|
-
bio: 'A test agent for unit testing',
|
|
117
|
-
templates: {
|
|
118
|
-
messageHandlerTemplate: 'Test template {{recentMessages}}',
|
|
119
|
-
shouldRespondTemplate: 'Should respond {{recentMessages}}',
|
|
120
|
-
},
|
|
121
|
-
} as Character,
|
|
122
|
-
|
|
123
|
-
// Core methods with default implementations
|
|
124
|
-
useModel: mock().mockResolvedValue('Mock response'),
|
|
125
|
-
composeState: mock().mockResolvedValue({
|
|
126
|
-
values: {
|
|
127
|
-
agentName: 'Test Agent',
|
|
128
|
-
recentMessages: 'Test message',
|
|
129
|
-
},
|
|
130
|
-
data: {
|
|
131
|
-
room: {
|
|
132
|
-
id: 'test-room-id',
|
|
133
|
-
type: ChannelType.DIRECT,
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
}),
|
|
137
|
-
createMemory: mock().mockResolvedValue({ id: 'memory-id' }),
|
|
138
|
-
getMemories: mock().mockResolvedValue([]),
|
|
139
|
-
searchMemories: mock().mockResolvedValue([]),
|
|
140
|
-
updateMemory: mock().mockResolvedValue(undefined),
|
|
141
|
-
getSetting: mock().mockImplementation((key: string) => {
|
|
142
|
-
const settings: Record<string, string> = {
|
|
143
|
-
TEST_SETTING: 'test-value',
|
|
144
|
-
API_KEY: 'test-api-key',
|
|
145
|
-
// Add common settings your plugin might need
|
|
146
|
-
};
|
|
147
|
-
return settings[key];
|
|
148
|
-
}),
|
|
149
|
-
getRoom: mock().mockResolvedValue({
|
|
150
|
-
id: 'test-room-id',
|
|
151
|
-
type: ChannelType.DIRECT,
|
|
152
|
-
worldId: 'test-world-id',
|
|
153
|
-
serverId: 'test-server-id',
|
|
154
|
-
source: 'test',
|
|
155
|
-
}),
|
|
156
|
-
getParticipantUserState: mock().mockResolvedValue('ACTIVE'),
|
|
157
|
-
setParticipantUserState: mock().mockResolvedValue(undefined),
|
|
158
|
-
emitEvent: mock().mockResolvedValue(undefined),
|
|
159
|
-
getTasks: mock().mockResolvedValue([]),
|
|
160
|
-
|
|
161
|
-
// Provider/action/evaluator lists
|
|
162
|
-
providers: [],
|
|
163
|
-
actions: [],
|
|
164
|
-
evaluators: [],
|
|
165
|
-
services: [],
|
|
166
|
-
|
|
167
|
-
// Override with custom implementations
|
|
168
|
-
...overrides,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Create Mock Memory
|
|
173
|
-
export function createMockMemory(overrides: Partial<Memory> = {}): Partial<Memory> {
|
|
174
|
-
return {
|
|
175
|
-
id: 'test-message-id' as UUID,
|
|
176
|
-
roomId: 'test-room-id' as UUID,
|
|
177
|
-
entityId: 'test-entity-id' as UUID,
|
|
178
|
-
agentId: 'test-agent-id' as UUID,
|
|
179
|
-
content: {
|
|
180
|
-
text: 'Test message',
|
|
181
|
-
channelType: ChannelType.DIRECT,
|
|
182
|
-
source: 'direct',
|
|
183
|
-
} as Content,
|
|
184
|
-
createdAt: Date.now(),
|
|
185
|
-
userId: 'test-user-id' as UUID,
|
|
186
|
-
...overrides,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Create Mock State
|
|
191
|
-
export function createMockState(overrides: Partial<State> = {}): Partial<State> {
|
|
192
|
-
return {
|
|
193
|
-
values: {
|
|
194
|
-
agentName: 'Test Agent',
|
|
195
|
-
recentMessages: 'User: Test message',
|
|
196
|
-
...overrides.values,
|
|
197
|
-
},
|
|
198
|
-
data: {
|
|
199
|
-
room: {
|
|
200
|
-
id: 'test-room-id',
|
|
201
|
-
type: ChannelType.DIRECT,
|
|
202
|
-
},
|
|
203
|
-
...overrides.data,
|
|
204
|
-
},
|
|
205
|
-
...overrides,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Setup Action Test Helper
|
|
210
|
-
export function setupActionTest(
|
|
211
|
-
options: {
|
|
212
|
-
runtimeOverrides?: Partial<MockRuntime>;
|
|
213
|
-
messageOverrides?: Partial<Memory>;
|
|
214
|
-
stateOverrides?: Partial<State>;
|
|
215
|
-
} = {}
|
|
216
|
-
) {
|
|
217
|
-
const mockRuntime = createMockRuntime(options.runtimeOverrides);
|
|
218
|
-
const mockMessage = createMockMemory(options.messageOverrides);
|
|
219
|
-
const mockState = createMockState(options.stateOverrides);
|
|
220
|
-
const callbackFn = mock().mockResolvedValue([]);
|
|
221
|
-
|
|
222
|
-
return {
|
|
223
|
-
mockRuntime,
|
|
224
|
-
mockMessage,
|
|
225
|
-
mockState,
|
|
226
|
-
callbackFn,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Mock Logger Helper
|
|
231
|
-
export function mockLogger() {
|
|
232
|
-
spyOn(logger, 'error').mockImplementation(() => {});
|
|
233
|
-
spyOn(logger, 'warn').mockImplementation(() => {});
|
|
234
|
-
spyOn(logger, 'info').mockImplementation(() => {});
|
|
235
|
-
spyOn(logger, 'debug').mockImplementation(() => {});
|
|
236
|
-
}
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## 3. Testing Actions
|
|
242
|
-
|
|
243
|
-
### Basic Action Test Structure
|
|
244
|
-
|
|
245
|
-
```typescript
|
|
246
|
-
// src/__tests__/actions.test.ts
|
|
247
|
-
import { describe, expect, it, mock, beforeEach, afterEach } from 'bun:test';
|
|
248
|
-
import { myAction } from '../actions/myAction';
|
|
249
|
-
import { setupActionTest, mockLogger } from './test-utils';
|
|
250
|
-
import type { MockRuntime } from './test-utils';
|
|
251
|
-
import {
|
|
252
|
-
type IAgentRuntime,
|
|
253
|
-
type Memory,
|
|
254
|
-
type State,
|
|
255
|
-
type HandlerCallback,
|
|
256
|
-
ModelType,
|
|
257
|
-
} from '@elizaos/core';
|
|
258
|
-
|
|
259
|
-
describe('My Action', () => {
|
|
260
|
-
let mockRuntime: MockRuntime;
|
|
261
|
-
let mockMessage: Partial<Memory>;
|
|
262
|
-
let mockState: Partial<State>;
|
|
263
|
-
let callbackFn: HandlerCallback;
|
|
264
|
-
|
|
265
|
-
beforeEach(() => {
|
|
266
|
-
mockLogger();
|
|
267
|
-
const setup = setupActionTest();
|
|
268
|
-
mockRuntime = setup.mockRuntime;
|
|
269
|
-
mockMessage = setup.mockMessage;
|
|
270
|
-
mockState = setup.mockState;
|
|
271
|
-
callbackFn = setup.callbackFn as HandlerCallback;
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
afterEach(() => {
|
|
275
|
-
mock.restore();
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe('validation', () => {
|
|
279
|
-
it('should validate when conditions are met', async () => {
|
|
280
|
-
// Setup message content that should validate
|
|
281
|
-
mockMessage.content = {
|
|
282
|
-
text: 'perform action',
|
|
283
|
-
channelType: 'direct',
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
const isValid = await myAction.validate(
|
|
287
|
-
mockRuntime as IAgentRuntime,
|
|
288
|
-
mockMessage as Memory,
|
|
289
|
-
mockState as State
|
|
290
|
-
);
|
|
291
|
-
|
|
292
|
-
expect(isValid).toBe(true);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should not validate when conditions are not met', async () => {
|
|
296
|
-
// Setup message content that should not validate
|
|
297
|
-
mockMessage.content = {
|
|
298
|
-
text: 'unrelated message',
|
|
299
|
-
channelType: 'direct',
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const isValid = await myAction.validate(
|
|
303
|
-
mockRuntime as IAgentRuntime,
|
|
304
|
-
mockMessage as Memory,
|
|
305
|
-
mockState as State
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
expect(isValid).toBe(false);
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
describe('handler', () => {
|
|
313
|
-
it('should handle action successfully', async () => {
|
|
314
|
-
// Mock runtime methods specific to this action
|
|
315
|
-
mockRuntime.useModel = mock().mockResolvedValue({
|
|
316
|
-
action: 'PERFORM',
|
|
317
|
-
parameters: { value: 'test' },
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const result = await myAction.handler(
|
|
321
|
-
mockRuntime as IAgentRuntime,
|
|
322
|
-
mockMessage as Memory,
|
|
323
|
-
mockState as State,
|
|
324
|
-
{},
|
|
325
|
-
callbackFn
|
|
326
|
-
);
|
|
327
|
-
|
|
328
|
-
expect(result).toBe(true);
|
|
329
|
-
expect(callbackFn).toHaveBeenCalledWith(
|
|
330
|
-
expect.objectContaining({
|
|
331
|
-
text: expect.any(String),
|
|
332
|
-
content: expect.any(Object),
|
|
333
|
-
})
|
|
334
|
-
);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
it('should handle errors gracefully', async () => {
|
|
338
|
-
// Mock an error scenario
|
|
339
|
-
mockRuntime.useModel = mock().mockRejectedValue(new Error('Model error'));
|
|
340
|
-
|
|
341
|
-
await myAction.handler(
|
|
342
|
-
mockRuntime as IAgentRuntime,
|
|
343
|
-
mockMessage as Memory,
|
|
344
|
-
mockState as State,
|
|
345
|
-
{},
|
|
346
|
-
callbackFn
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
expect(callbackFn).toHaveBeenCalledWith(
|
|
350
|
-
expect.objectContaining({
|
|
351
|
-
text: expect.stringContaining('error'),
|
|
352
|
-
})
|
|
353
|
-
);
|
|
354
|
-
});
|
|
355
|
-
});
|
|
356
|
-
});
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
### Testing Async Actions
|
|
360
|
-
|
|
361
|
-
```typescript
|
|
362
|
-
describe('Async Action', () => {
|
|
363
|
-
it('should handle async operations', async () => {
|
|
364
|
-
const setup = setupActionTest({
|
|
365
|
-
runtimeOverrides: {
|
|
366
|
-
useModel: mock().mockImplementation(async (modelType) => {
|
|
367
|
-
// Simulate async delay
|
|
368
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
369
|
-
return { result: 'async result' };
|
|
370
|
-
}),
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
const result = await asyncAction.handler(
|
|
375
|
-
setup.mockRuntime as IAgentRuntime,
|
|
376
|
-
setup.mockMessage as Memory,
|
|
377
|
-
setup.mockState as State,
|
|
378
|
-
{},
|
|
379
|
-
setup.callbackFn as HandlerCallback
|
|
380
|
-
);
|
|
381
|
-
|
|
382
|
-
expect(result).toBe(true);
|
|
383
|
-
expect(setup.callbackFn).toHaveBeenCalled();
|
|
384
|
-
});
|
|
385
|
-
});
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
---
|
|
389
|
-
|
|
390
|
-
## 4. Testing Providers
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
// src/__tests__/providers.test.ts
|
|
394
|
-
import { describe, expect, it, mock, beforeEach, afterEach } from 'bun:test';
|
|
395
|
-
import { myProvider } from '../providers/myProvider';
|
|
396
|
-
import { createMockRuntime, createMockMemory, createMockState } from './test-utils';
|
|
397
|
-
import { type IAgentRuntime, type Memory, type State } from '@elizaos/core';
|
|
398
|
-
|
|
399
|
-
describe('My Provider', () => {
|
|
400
|
-
let mockRuntime: any;
|
|
401
|
-
let mockMessage: Partial<Memory>;
|
|
402
|
-
let mockState: Partial<State>;
|
|
403
|
-
|
|
404
|
-
beforeEach(() => {
|
|
405
|
-
mockRuntime = createMockRuntime();
|
|
406
|
-
mockMessage = createMockMemory();
|
|
407
|
-
mockState = createMockState();
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
afterEach(() => {
|
|
411
|
-
mock.restore();
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it('should have required properties', () => {
|
|
415
|
-
expect(myProvider.name).toBe('MY_PROVIDER');
|
|
416
|
-
expect(myProvider.get).toBeDefined();
|
|
417
|
-
expect(typeof myProvider.get).toBe('function');
|
|
418
|
-
});
|
|
419
|
-
|
|
420
|
-
it('should return data in correct format', async () => {
|
|
421
|
-
// Mock any runtime methods the provider uses
|
|
422
|
-
mockRuntime.getMemories = mock().mockResolvedValue([
|
|
423
|
-
{ content: { text: 'Memory 1' }, createdAt: Date.now() },
|
|
424
|
-
{ content: { text: 'Memory 2' }, createdAt: Date.now() - 1000 },
|
|
425
|
-
]);
|
|
426
|
-
|
|
427
|
-
const result = await myProvider.get(
|
|
428
|
-
mockRuntime as IAgentRuntime,
|
|
429
|
-
mockMessage as Memory,
|
|
430
|
-
mockState as State
|
|
431
|
-
);
|
|
432
|
-
|
|
433
|
-
expect(result).toMatchObject({
|
|
434
|
-
text: expect.any(String),
|
|
435
|
-
data: expect.any(Object),
|
|
436
|
-
});
|
|
437
|
-
});
|
|
438
|
-
|
|
439
|
-
it('should handle empty data gracefully', async () => {
|
|
440
|
-
mockRuntime.getMemories = mock().mockResolvedValue([]);
|
|
441
|
-
|
|
442
|
-
const result = await myProvider.get(
|
|
443
|
-
mockRuntime as IAgentRuntime,
|
|
444
|
-
mockMessage as Memory,
|
|
445
|
-
mockState as State
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
expect(result).toBeDefined();
|
|
449
|
-
expect(result.text).toContain('No data available');
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
it('should handle errors gracefully', async () => {
|
|
453
|
-
mockRuntime.getMemories = mock().mockRejectedValue(new Error('Database error'));
|
|
454
|
-
|
|
455
|
-
const result = await myProvider.get(
|
|
456
|
-
mockRuntime as IAgentRuntime,
|
|
457
|
-
mockMessage as Memory,
|
|
458
|
-
mockState as State
|
|
459
|
-
);
|
|
460
|
-
|
|
461
|
-
expect(result).toBeDefined();
|
|
462
|
-
expect(result.text).toContain('Error retrieving data');
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
---
|
|
468
|
-
|
|
469
|
-
## 5. Testing Evaluators
|
|
470
|
-
|
|
471
|
-
```typescript
|
|
472
|
-
// src/__tests__/evaluators.test.ts
|
|
473
|
-
import { describe, expect, it, mock, beforeEach, afterEach } from 'bun:test';
|
|
474
|
-
import { myEvaluator } from '../evaluators/myEvaluator';
|
|
475
|
-
import { createMockRuntime, createMockMemory, createMockState } from './test-utils';
|
|
476
|
-
import { type IAgentRuntime, type Memory, type State } from '@elizaos/core';
|
|
477
|
-
|
|
478
|
-
describe('My Evaluator', () => {
|
|
479
|
-
let mockRuntime: any;
|
|
480
|
-
let mockMessage: Partial<Memory>;
|
|
481
|
-
let mockState: Partial<State>;
|
|
482
|
-
|
|
483
|
-
beforeEach(() => {
|
|
484
|
-
mockRuntime = createMockRuntime();
|
|
485
|
-
mockMessage = createMockMemory();
|
|
486
|
-
mockState = createMockState();
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
afterEach(() => {
|
|
490
|
-
mock.restore();
|
|
491
|
-
});
|
|
492
|
-
|
|
493
|
-
it('should have required properties', () => {
|
|
494
|
-
expect(myEvaluator.name).toBe('MY_EVALUATOR');
|
|
495
|
-
expect(myEvaluator.evaluate).toBeDefined();
|
|
496
|
-
expect(myEvaluator.validate).toBeDefined();
|
|
497
|
-
});
|
|
498
|
-
|
|
499
|
-
it('should validate when conditions are met', async () => {
|
|
500
|
-
const isValid = await myEvaluator.validate(
|
|
501
|
-
mockRuntime as IAgentRuntime,
|
|
502
|
-
mockMessage as Memory,
|
|
503
|
-
mockState as State
|
|
504
|
-
);
|
|
505
|
-
|
|
506
|
-
expect(isValid).toBe(true);
|
|
507
|
-
});
|
|
508
|
-
|
|
509
|
-
it('should evaluate and create memory', async () => {
|
|
510
|
-
mockRuntime.createMemory = mock().mockResolvedValue({ id: 'new-memory-id' });
|
|
511
|
-
|
|
512
|
-
await myEvaluator.evaluate(
|
|
513
|
-
mockRuntime as IAgentRuntime,
|
|
514
|
-
mockMessage as Memory,
|
|
515
|
-
mockState as State,
|
|
516
|
-
{}
|
|
517
|
-
);
|
|
518
|
-
|
|
519
|
-
expect(mockRuntime.createMemory).toHaveBeenCalledWith(
|
|
520
|
-
expect.objectContaining({
|
|
521
|
-
content: expect.objectContaining({
|
|
522
|
-
text: expect.any(String),
|
|
523
|
-
}),
|
|
524
|
-
}),
|
|
525
|
-
expect.any(String) // tableName
|
|
526
|
-
);
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
it('should not create memory when evaluation fails', async () => {
|
|
530
|
-
// Mock a scenario where evaluation should fail
|
|
531
|
-
mockMessage.content = { text: 'invalid content' };
|
|
532
|
-
|
|
533
|
-
await myEvaluator.evaluate(
|
|
534
|
-
mockRuntime as IAgentRuntime,
|
|
535
|
-
mockMessage as Memory,
|
|
536
|
-
mockState as State,
|
|
537
|
-
{}
|
|
538
|
-
);
|
|
539
|
-
|
|
540
|
-
expect(mockRuntime.createMemory).not.toHaveBeenCalled();
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
---
|
|
546
|
-
|
|
547
|
-
## 6. Testing Services
|
|
548
|
-
|
|
549
|
-
```typescript
|
|
550
|
-
// src/__tests__/services.test.ts
|
|
551
|
-
import { describe, expect, it, mock, beforeEach, afterEach } from 'bun:test';
|
|
552
|
-
import { myService } from '../services/myService';
|
|
553
|
-
import { createMockRuntime } from './test-utils';
|
|
554
|
-
import { type IAgentRuntime } from '@elizaos/core';
|
|
555
|
-
|
|
556
|
-
describe('My Service', () => {
|
|
557
|
-
let mockRuntime: any;
|
|
558
|
-
|
|
559
|
-
beforeEach(() => {
|
|
560
|
-
mockRuntime = createMockRuntime();
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
afterEach(() => {
|
|
564
|
-
mock.restore();
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
it('should initialize service', async () => {
|
|
568
|
-
const service = await myService.initialize(mockRuntime as IAgentRuntime);
|
|
569
|
-
|
|
570
|
-
expect(service).toBeDefined();
|
|
571
|
-
expect(service.start).toBeDefined();
|
|
572
|
-
expect(service.stop).toBeDefined();
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
it('should start service successfully', async () => {
|
|
576
|
-
const service = await myService.initialize(mockRuntime as IAgentRuntime);
|
|
577
|
-
const startSpy = mock(service.start);
|
|
578
|
-
|
|
579
|
-
await service.start();
|
|
580
|
-
|
|
581
|
-
expect(startSpy).toHaveBeenCalled();
|
|
582
|
-
});
|
|
583
|
-
|
|
584
|
-
it('should stop service successfully', async () => {
|
|
585
|
-
const service = await myService.initialize(mockRuntime as IAgentRuntime);
|
|
586
|
-
await service.start();
|
|
587
|
-
|
|
588
|
-
const stopSpy = mock(service.stop);
|
|
589
|
-
await service.stop();
|
|
590
|
-
|
|
591
|
-
expect(stopSpy).toHaveBeenCalled();
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
it('should handle service errors', async () => {
|
|
595
|
-
const service = await myService.initialize(mockRuntime as IAgentRuntime);
|
|
596
|
-
service.start = mock().mockRejectedValue(new Error('Service start failed'));
|
|
597
|
-
|
|
598
|
-
await expect(service.start()).rejects.toThrow('Service start failed');
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
```
|
|
602
|
-
|
|
603
|
-
---
|
|
604
|
-
|
|
605
|
-
## 7. Testing Event Handlers
|
|
606
|
-
|
|
607
|
-
```typescript
|
|
608
|
-
// src/__tests__/events.test.ts
|
|
609
|
-
import { describe, expect, it, mock, beforeEach, afterEach } from 'bun:test';
|
|
610
|
-
import { myPlugin } from '../index';
|
|
611
|
-
import { setupActionTest } from './test-utils';
|
|
612
|
-
import {
|
|
613
|
-
type IAgentRuntime,
|
|
614
|
-
type Memory,
|
|
615
|
-
EventType,
|
|
616
|
-
type MessagePayload,
|
|
617
|
-
type EntityPayload,
|
|
618
|
-
} from '@elizaos/core';
|
|
619
|
-
|
|
620
|
-
describe('Event Handlers', () => {
|
|
621
|
-
let mockRuntime: any;
|
|
622
|
-
let mockMessage: Partial<Memory>;
|
|
623
|
-
let mockCallback: any;
|
|
624
|
-
|
|
625
|
-
beforeEach(() => {
|
|
626
|
-
const setup = setupActionTest();
|
|
627
|
-
mockRuntime = setup.mockRuntime;
|
|
628
|
-
mockMessage = setup.mockMessage;
|
|
629
|
-
mockCallback = setup.callbackFn;
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
afterEach(() => {
|
|
633
|
-
mock.restore();
|
|
634
|
-
});
|
|
635
|
-
|
|
636
|
-
it('should handle MESSAGE_RECEIVED event', async () => {
|
|
637
|
-
const messageHandler = myPlugin.events?.[EventType.MESSAGE_RECEIVED]?.[0];
|
|
638
|
-
expect(messageHandler).toBeDefined();
|
|
639
|
-
|
|
640
|
-
if (messageHandler) {
|
|
641
|
-
await messageHandler({
|
|
642
|
-
runtime: mockRuntime as IAgentRuntime,
|
|
643
|
-
message: mockMessage as Memory,
|
|
644
|
-
callback: mockCallback,
|
|
645
|
-
source: 'test',
|
|
646
|
-
} as MessagePayload);
|
|
647
|
-
|
|
648
|
-
expect(mockRuntime.createMemory).toHaveBeenCalledWith(mockMessage, 'messages');
|
|
649
|
-
}
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
it('should handle ENTITY_JOINED event', async () => {
|
|
653
|
-
const entityHandler = myPlugin.events?.[EventType.ENTITY_JOINED]?.[0];
|
|
654
|
-
expect(entityHandler).toBeDefined();
|
|
655
|
-
|
|
656
|
-
if (entityHandler) {
|
|
657
|
-
await entityHandler({
|
|
658
|
-
runtime: mockRuntime as IAgentRuntime,
|
|
659
|
-
entityId: 'test-entity-id',
|
|
660
|
-
worldId: 'test-world-id',
|
|
661
|
-
roomId: 'test-room-id',
|
|
662
|
-
metadata: {
|
|
663
|
-
type: 'user',
|
|
664
|
-
username: 'testuser',
|
|
665
|
-
},
|
|
666
|
-
source: 'test',
|
|
667
|
-
} as EntityPayload);
|
|
668
|
-
|
|
669
|
-
expect(mockRuntime.ensureConnection).toHaveBeenCalled();
|
|
670
|
-
}
|
|
671
|
-
});
|
|
672
|
-
});
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
---
|
|
676
|
-
|
|
677
|
-
## 8. Advanced Testing Patterns
|
|
678
|
-
|
|
679
|
-
### Testing with Complex State
|
|
680
|
-
|
|
681
|
-
```typescript
|
|
682
|
-
describe('Complex State Action', () => {
|
|
683
|
-
it('should handle complex state transformations', async () => {
|
|
684
|
-
const setup = setupActionTest({
|
|
685
|
-
stateOverrides: {
|
|
686
|
-
values: {
|
|
687
|
-
taskList: ['task1', 'task2'],
|
|
688
|
-
currentStep: 2,
|
|
689
|
-
metadata: { key: 'value' },
|
|
690
|
-
},
|
|
691
|
-
data: {
|
|
692
|
-
customData: {
|
|
693
|
-
nested: {
|
|
694
|
-
value: 'deep',
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
},
|
|
699
|
-
});
|
|
700
|
-
|
|
701
|
-
const result = await complexAction.handler(
|
|
702
|
-
setup.mockRuntime as IAgentRuntime,
|
|
703
|
-
setup.mockMessage as Memory,
|
|
704
|
-
setup.mockState as State,
|
|
705
|
-
{},
|
|
706
|
-
setup.callbackFn as HandlerCallback
|
|
707
|
-
);
|
|
708
|
-
|
|
709
|
-
expect(result).toBe(true);
|
|
710
|
-
});
|
|
711
|
-
});
|
|
712
|
-
```
|
|
713
|
-
|
|
714
|
-
### Testing with Multiple Mock Responses
|
|
715
|
-
|
|
716
|
-
```typescript
|
|
717
|
-
describe('Sequential Operations', () => {
|
|
718
|
-
it('should handle sequential API calls', async () => {
|
|
719
|
-
const setup = setupActionTest({
|
|
720
|
-
runtimeOverrides: {
|
|
721
|
-
useModel: mock()
|
|
722
|
-
.mockResolvedValueOnce({ step: 1, data: 'first' })
|
|
723
|
-
.mockResolvedValueOnce({ step: 2, data: 'second' })
|
|
724
|
-
.mockResolvedValueOnce({ step: 3, data: 'final' }),
|
|
725
|
-
},
|
|
726
|
-
});
|
|
727
|
-
|
|
728
|
-
await sequentialAction.handler(
|
|
729
|
-
setup.mockRuntime as IAgentRuntime,
|
|
730
|
-
setup.mockMessage as Memory,
|
|
731
|
-
setup.mockState as State,
|
|
732
|
-
{},
|
|
733
|
-
setup.callbackFn as HandlerCallback
|
|
734
|
-
);
|
|
735
|
-
|
|
736
|
-
expect(setup.mockRuntime.useModel).toHaveBeenCalledTimes(3);
|
|
737
|
-
expect(setup.callbackFn).toHaveBeenCalledWith(
|
|
738
|
-
expect.objectContaining({
|
|
739
|
-
text: expect.stringContaining('final'),
|
|
740
|
-
})
|
|
741
|
-
);
|
|
742
|
-
});
|
|
743
|
-
});
|
|
744
|
-
```
|
|
745
|
-
|
|
746
|
-
### Testing Error Recovery
|
|
747
|
-
|
|
748
|
-
```typescript
|
|
749
|
-
describe('Error Recovery', () => {
|
|
750
|
-
it('should retry on failure', async () => {
|
|
751
|
-
let attempts = 0;
|
|
752
|
-
const setup = setupActionTest({
|
|
753
|
-
runtimeOverrides: {
|
|
754
|
-
useModel: mock().mockImplementation(async () => {
|
|
755
|
-
attempts++;
|
|
756
|
-
if (attempts < 3) {
|
|
757
|
-
throw new Error('Temporary failure');
|
|
758
|
-
}
|
|
759
|
-
return { success: true };
|
|
760
|
-
}),
|
|
761
|
-
},
|
|
762
|
-
});
|
|
763
|
-
|
|
764
|
-
await retryAction.handler(
|
|
765
|
-
setup.mockRuntime as IAgentRuntime,
|
|
766
|
-
setup.mockMessage as Memory,
|
|
767
|
-
setup.mockState as State,
|
|
768
|
-
{},
|
|
769
|
-
setup.callbackFn as HandlerCallback
|
|
770
|
-
);
|
|
771
|
-
|
|
772
|
-
expect(attempts).toBe(3);
|
|
773
|
-
expect(setup.callbackFn).toHaveBeenCalledWith(
|
|
774
|
-
expect.objectContaining({
|
|
775
|
-
content: expect.objectContaining({ success: true }),
|
|
776
|
-
})
|
|
777
|
-
);
|
|
778
|
-
});
|
|
779
|
-
});
|
|
780
|
-
```
|
|
781
|
-
|
|
782
|
-
---
|
|
783
|
-
|
|
784
|
-
## 9. Best Practices
|
|
785
|
-
|
|
786
|
-
### 1. Test Organization
|
|
787
|
-
|
|
788
|
-
- Group related tests using `describe` blocks
|
|
789
|
-
- Use clear, descriptive test names
|
|
790
|
-
- Follow the Arrange-Act-Assert pattern
|
|
791
|
-
- Keep tests focused and independent
|
|
792
|
-
|
|
793
|
-
### 2. Mock Management
|
|
794
|
-
|
|
795
|
-
```typescript
|
|
796
|
-
// Good: Specific mocks for each test
|
|
797
|
-
it('should handle specific scenario', async () => {
|
|
798
|
-
const setup = setupActionTest({
|
|
799
|
-
runtimeOverrides: {
|
|
800
|
-
useModel: mock().mockResolvedValue({ specific: 'response' }),
|
|
801
|
-
},
|
|
802
|
-
});
|
|
803
|
-
// ... test implementation
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
// Bad: Global mocks that affect all tests
|
|
807
|
-
beforeAll(() => {
|
|
808
|
-
globalMock = mock().mockResolvedValue('global response');
|
|
809
|
-
});
|
|
810
|
-
```
|
|
811
|
-
|
|
812
|
-
### 3. Assertion Patterns
|
|
813
|
-
|
|
814
|
-
```typescript
|
|
815
|
-
// Check callback was called with correct structure
|
|
816
|
-
expect(callbackFn).toHaveBeenCalledWith(
|
|
817
|
-
expect.objectContaining({
|
|
818
|
-
text: expect.stringContaining('expected text'),
|
|
819
|
-
content: expect.objectContaining({
|
|
820
|
-
success: true,
|
|
821
|
-
data: expect.arrayContaining(['item1', 'item2']),
|
|
822
|
-
}),
|
|
823
|
-
})
|
|
824
|
-
);
|
|
825
|
-
|
|
826
|
-
// Check multiple calls in sequence
|
|
827
|
-
const calls = (callbackFn as any).mock.calls;
|
|
828
|
-
expect(calls).toHaveLength(3);
|
|
829
|
-
expect(calls[0][0].text).toContain('step 1');
|
|
830
|
-
expect(calls[1][0].text).toContain('step 2');
|
|
831
|
-
expect(calls[2][0].text).toContain('completed');
|
|
832
|
-
```
|
|
833
|
-
|
|
834
|
-
### 4. Testing Edge Cases
|
|
835
|
-
|
|
836
|
-
```typescript
|
|
837
|
-
describe('Edge Cases', () => {
|
|
838
|
-
it('should handle empty input', async () => {
|
|
839
|
-
mockMessage.content = { text: '' };
|
|
840
|
-
// ... test implementation
|
|
841
|
-
});
|
|
842
|
-
|
|
843
|
-
it('should handle null values', async () => {
|
|
844
|
-
mockMessage.content = null as any;
|
|
845
|
-
// ... test implementation
|
|
846
|
-
});
|
|
847
|
-
|
|
848
|
-
it('should handle very long input', async () => {
|
|
849
|
-
mockMessage.content = { text: 'a'.repeat(10000) };
|
|
850
|
-
// ... test implementation
|
|
851
|
-
});
|
|
852
|
-
});
|
|
853
|
-
```
|
|
854
|
-
|
|
855
|
-
### 5. Async Testing Best Practices
|
|
856
|
-
|
|
857
|
-
```typescript
|
|
858
|
-
// Always await async operations
|
|
859
|
-
it('should handle async operations', async () => {
|
|
860
|
-
const promise = someAsyncOperation();
|
|
861
|
-
await expect(promise).resolves.toBe(expectedValue);
|
|
862
|
-
});
|
|
863
|
-
|
|
864
|
-
// Test rejected promises
|
|
865
|
-
it('should handle errors', async () => {
|
|
866
|
-
const promise = failingOperation();
|
|
867
|
-
await expect(promise).rejects.toThrow('Expected error');
|
|
868
|
-
});
|
|
869
|
-
|
|
870
|
-
// Use async/await instead of .then()
|
|
871
|
-
it('should process data', async () => {
|
|
872
|
-
const result = await processData();
|
|
873
|
-
expect(result).toBeDefined();
|
|
874
|
-
});
|
|
875
|
-
```
|
|
876
|
-
|
|
877
|
-
### 6. Cleanup
|
|
878
|
-
|
|
879
|
-
```typescript
|
|
880
|
-
afterEach(() => {
|
|
881
|
-
// Reset all mocks after each test
|
|
882
|
-
mock.restore();
|
|
883
|
-
|
|
884
|
-
// Clean up any side effects
|
|
885
|
-
// Clear timers, close connections, etc.
|
|
886
|
-
});
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
### 7. Test Coverage Requirements
|
|
890
|
-
|
|
891
|
-
**IMPORTANT**: All ElizaOS plugins must maintain 100% test coverage or as close to it as possible (minimum 95%).
|
|
892
|
-
|
|
893
|
-
```typescript
|
|
894
|
-
// Ensure all code paths are tested
|
|
895
|
-
describe('Complete Coverage', () => {
|
|
896
|
-
it('should test success path', async () => {
|
|
897
|
-
// Test the happy path
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
it('should test error handling', async () => {
|
|
901
|
-
// Test error scenarios
|
|
902
|
-
});
|
|
903
|
-
|
|
904
|
-
it('should test edge cases', async () => {
|
|
905
|
-
// Test boundary conditions
|
|
906
|
-
});
|
|
907
|
-
|
|
908
|
-
it('should test all conditional branches', async () => {
|
|
909
|
-
// Test if/else, switch cases, etc.
|
|
910
|
-
});
|
|
911
|
-
});
|
|
912
|
-
```
|
|
913
|
-
|
|
914
|
-
**Coverage Best Practices:**
|
|
915
|
-
|
|
916
|
-
- Use `bun test --coverage` to check coverage regularly
|
|
917
|
-
- Set up CI/CD to fail builds with coverage below 95%
|
|
918
|
-
- Document any legitimate reasons for uncovered code
|
|
919
|
-
- Focus on meaningful tests, not just hitting coverage numbers
|
|
920
|
-
- Test all exports from your plugin (actions, providers, evaluators, services)
|
|
921
|
-
|
|
922
|
-
---
|
|
923
|
-
|
|
924
|
-
## 10. Running Tests
|
|
925
|
-
|
|
926
|
-
### Basic Commands
|
|
927
|
-
|
|
928
|
-
```bash
|
|
929
|
-
# Run all tests
|
|
930
|
-
bun run test
|
|
931
|
-
|
|
932
|
-
# Run tests in watch mode
|
|
933
|
-
bun run test --watch
|
|
934
|
-
|
|
935
|
-
# Run specific test file
|
|
936
|
-
bun run test src/__tests__/actions.test.ts
|
|
937
|
-
|
|
938
|
-
# Run tests with coverage
|
|
939
|
-
bun run test:coverage
|
|
940
|
-
|
|
941
|
-
# Run tests matching pattern
|
|
942
|
-
bun run test --test-name-pattern "should validate"
|
|
943
|
-
```
|
|
944
|
-
|
|
945
|
-
### Test Configuration
|
|
946
|
-
|
|
947
|
-
Create a `bunfig.toml` file in your project root:
|
|
948
|
-
|
|
949
|
-
```toml
|
|
950
|
-
[test]
|
|
951
|
-
root = "./src/__tests__"
|
|
952
|
-
coverage = true
|
|
953
|
-
coverageThreshold = 95 # Minimum 95% coverage required, aim for 100%
|
|
954
|
-
```
|
|
955
|
-
|
|
956
|
-
### Debugging Tests
|
|
957
|
-
|
|
958
|
-
```typescript
|
|
959
|
-
// Add console.logs for debugging
|
|
960
|
-
it('should debug test', async () => {
|
|
961
|
-
console.log('Current state:', mockState);
|
|
962
|
-
|
|
963
|
-
const result = await action.handler(...);
|
|
964
|
-
|
|
965
|
-
console.log('Result:', result);
|
|
966
|
-
console.log('Callback calls:', (callbackFn as any).mock.calls);
|
|
967
|
-
});
|
|
968
|
-
```
|
|
969
|
-
|
|
970
|
-
### Common Issues and Solutions
|
|
971
|
-
|
|
972
|
-
#### Issue: Mock not being called
|
|
973
|
-
|
|
974
|
-
```typescript
|
|
975
|
-
// Solution: Ensure the mock is set before the action is called
|
|
976
|
-
mockRuntime.useModel = mock().mockResolvedValue(response);
|
|
977
|
-
// THEN call the action
|
|
978
|
-
await action.handler(...);
|
|
979
|
-
```
|
|
980
|
-
|
|
981
|
-
#### Issue: Tests timing out
|
|
982
|
-
|
|
983
|
-
```typescript
|
|
984
|
-
// Solution: Mock all async dependencies
|
|
985
|
-
beforeEach(() => {
|
|
986
|
-
// Mock all external calls
|
|
987
|
-
mockRuntime.getMemories = mock().mockResolvedValue([]);
|
|
988
|
-
mockRuntime.searchMemories = mock().mockResolvedValue([]);
|
|
989
|
-
mockRuntime.createMemory = mock().mockResolvedValue({ id: 'test' });
|
|
990
|
-
});
|
|
991
|
-
```
|
|
992
|
-
|
|
993
|
-
#### Issue: Inconsistent test results
|
|
994
|
-
|
|
995
|
-
```typescript
|
|
996
|
-
// Solution: Reset mocks between tests
|
|
997
|
-
afterEach(() => {
|
|
998
|
-
mock.restore();
|
|
999
|
-
});
|
|
1000
|
-
|
|
1001
|
-
// And use fresh setup for each test
|
|
1002
|
-
beforeEach(() => {
|
|
1003
|
-
const setup = setupActionTest();
|
|
1004
|
-
// ... assign to test variables
|
|
1005
|
-
});
|
|
1006
|
-
```
|
|
1007
|
-
|
|
1008
|
-
---
|
|
1009
|
-
|
|
1010
|
-
## Summary
|
|
1011
|
-
|
|
1012
|
-
This guide provides a comprehensive approach to testing ElizaOS plugins. Key takeaways:
|
|
1013
|
-
|
|
1014
|
-
1. **Setup a consistent test environment** with reusable utilities
|
|
1015
|
-
2. **Test all plugin components**: actions, providers, evaluators, services, and event handlers
|
|
1016
|
-
3. **Mock external dependencies** properly to ensure isolated tests
|
|
1017
|
-
4. **Handle async operations** correctly with proper awaits
|
|
1018
|
-
5. **Follow best practices** for organization, assertions, and cleanup
|
|
1019
|
-
6. **Run tests regularly** as part of your development workflow
|
|
1020
|
-
|
|
1021
|
-
Remember: Good tests are as important as good code. They ensure your plugin works correctly and continues to work as the codebase evolves.
|