@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.
Files changed (148) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-2CUIHNPL.js → chunk-5GUS4CFO.js} +7 -2
  3. package/dist/{chunk-2ALAPQLV.js → chunk-E6XYTE3A.js} +296 -311
  4. package/dist/chunk-GXWWPFBO.js +39 -0
  5. package/dist/{chunk-I77ZRNYO.js → chunk-T2QDIXGU.js} +2 -2
  6. package/dist/commands/agent/actions/index.d.ts +5 -0
  7. package/dist/commands/agent/actions/index.js +2 -2
  8. package/dist/commands/agent/index.d.ts +2 -2
  9. package/dist/commands/agent/index.js +2 -2
  10. package/dist/commands/create/actions/index.js +3 -3
  11. package/dist/commands/create/index.js +4 -4
  12. package/dist/commands/shared/index.d.ts +11 -28
  13. package/dist/commands/shared/index.js +7 -3
  14. package/dist/index.js +541 -450
  15. package/dist/{registry-N626N4VG.js → registry-433S5F3Y.js} +2 -2
  16. package/dist/templates/plugin-quick-starter/.gitignore +66 -0
  17. package/dist/templates/plugin-quick-starter/.npmignore +5 -0
  18. package/dist/templates/plugin-quick-starter/package.json +11 -3
  19. package/dist/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  20. package/dist/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  21. package/dist/templates/plugin-quick-starter/src/plugin.ts +7 -13
  22. package/dist/templates/plugin-starter/.gitignore +66 -0
  23. package/dist/templates/plugin-starter/.npmignore +5 -0
  24. package/dist/templates/plugin-starter/README.md +1 -1
  25. package/dist/templates/plugin-starter/package.json +11 -3
  26. package/dist/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  27. package/dist/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  28. package/dist/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  29. package/dist/templates/plugin-starter/src/plugin.ts +18 -22
  30. package/dist/templates/project-starter/.gitignore +57 -0
  31. package/dist/templates/project-starter/.npmignore +11 -0
  32. package/dist/templates/project-starter/README.md +1 -1
  33. package/dist/templates/project-starter/package.json +4 -4
  34. package/dist/templates/project-starter/src/__tests__/env.test.ts +3 -1
  35. package/dist/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  36. package/dist/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  37. package/dist/templates/project-starter/tsup.config.ts +2 -1
  38. package/dist/templates/project-tee-starter/.dockerignore +64 -14
  39. package/dist/templates/project-tee-starter/.gitignore +57 -0
  40. package/dist/templates/project-tee-starter/.npmignore +6 -0
  41. package/dist/templates/project-tee-starter/Dockerfile +9 -5
  42. package/dist/templates/project-tee-starter/GUIDE.md +103 -42
  43. package/dist/templates/project-tee-starter/README.md +39 -19
  44. package/dist/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  45. package/dist/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  46. package/dist/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  47. package/dist/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  48. package/dist/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  49. package/dist/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  50. package/dist/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  51. package/dist/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  52. package/dist/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  53. package/dist/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  54. package/dist/templates/project-tee-starter/docker-compose.yaml +5 -2
  55. package/dist/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  56. package/dist/templates/project-tee-starter/package.json +34 -14
  57. package/dist/templates/project-tee-starter/postcss.config.js +3 -0
  58. package/dist/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  59. package/dist/templates/project-tee-starter/scripts/test-all.sh +82 -0
  60. package/dist/templates/project-tee-starter/src/frontend/index.css +106 -0
  61. package/dist/templates/project-tee-starter/src/frontend/index.html +20 -0
  62. package/dist/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  63. package/dist/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  64. package/dist/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  65. package/dist/templates/project-tee-starter/src/index.ts +6 -6
  66. package/dist/templates/project-tee-starter/src/plugin.ts +209 -59
  67. package/dist/templates/project-tee-starter/tailwind.config.js +62 -0
  68. package/dist/templates/project-tee-starter/tsconfig.build.json +2 -2
  69. package/dist/templates/project-tee-starter/tsconfig.json +8 -5
  70. package/dist/templates/project-tee-starter/tsup.config.ts +3 -2
  71. package/dist/templates/project-tee-starter/vite.config.ts +39 -0
  72. package/dist/url-utils-CKc_Ebt_.d.ts +35 -0
  73. package/dist/{utils-H66532NB.js → utils-DBLSDYBF.js} +2 -2
  74. package/package.json +12 -7
  75. package/templates/plugin-quick-starter/.gitignore +66 -0
  76. package/templates/plugin-quick-starter/.npmignore +5 -0
  77. package/templates/plugin-quick-starter/package.json +11 -3
  78. package/templates/plugin-quick-starter/src/__tests__/plugin.test.ts +499 -146
  79. package/templates/plugin-quick-starter/src/__tests__/test-utils.ts +316 -115
  80. package/templates/plugin-quick-starter/src/plugin.ts +7 -13
  81. package/templates/plugin-starter/.gitignore +66 -0
  82. package/templates/plugin-starter/.npmignore +5 -0
  83. package/templates/plugin-starter/README.md +1 -1
  84. package/templates/plugin-starter/package.json +11 -3
  85. package/templates/plugin-starter/src/__tests__/integration.test.ts +13 -13
  86. package/templates/plugin-starter/src/__tests__/plugin.test.ts +556 -129
  87. package/templates/plugin-starter/src/__tests__/test-utils.ts +347 -115
  88. package/templates/plugin-starter/src/plugin.ts +18 -22
  89. package/templates/project-starter/.gitignore +57 -0
  90. package/templates/project-starter/.npmignore +11 -0
  91. package/templates/project-starter/README.md +1 -1
  92. package/templates/project-starter/package.json +4 -4
  93. package/templates/project-starter/src/__tests__/env.test.ts +3 -1
  94. package/templates/project-starter/src/__tests__/file-structure.test.ts +3 -2
  95. package/templates/project-starter/src/__tests__/integration.test.ts +1 -1
  96. package/templates/project-starter/tsup.config.ts +2 -1
  97. package/templates/project-tee-starter/.dockerignore +64 -14
  98. package/templates/project-tee-starter/.gitignore +57 -0
  99. package/templates/project-tee-starter/.npmignore +6 -0
  100. package/templates/project-tee-starter/Dockerfile +9 -5
  101. package/templates/project-tee-starter/GUIDE.md +103 -42
  102. package/templates/project-tee-starter/README.md +39 -19
  103. package/templates/project-tee-starter/__tests__/build-order.test.ts +62 -0
  104. package/templates/project-tee-starter/__tests__/character.test.ts +19 -17
  105. package/templates/project-tee-starter/__tests__/config.test.ts +10 -3
  106. package/templates/project-tee-starter/__tests__/env.test.ts +2 -1
  107. package/templates/project-tee-starter/__tests__/file-structure.test.ts +14 -3
  108. package/templates/project-tee-starter/__tests__/frontend.test.ts +459 -0
  109. package/templates/project-tee-starter/__tests__/plugin.test.ts +4 -2
  110. package/templates/project-tee-starter/__tests__/routes.test.ts +15 -6
  111. package/templates/project-tee-starter/__tests__/tee-validation.test.ts +295 -0
  112. package/templates/project-tee-starter/__tests__/vite-config-utils.ts +39 -0
  113. package/templates/project-tee-starter/docker-compose.yaml +5 -2
  114. package/templates/{plugin-starter/dist → project-tee-starter}/index.html +3 -3
  115. package/templates/project-tee-starter/package.json +34 -14
  116. package/templates/project-tee-starter/postcss.config.js +3 -0
  117. package/templates/project-tee-starter/scripts/install-test-deps.js +52 -0
  118. package/templates/project-tee-starter/scripts/test-all.sh +82 -0
  119. package/templates/project-tee-starter/src/frontend/index.css +106 -0
  120. package/templates/project-tee-starter/src/frontend/index.html +20 -0
  121. package/templates/project-tee-starter/src/frontend/index.tsx +370 -0
  122. package/templates/project-tee-starter/src/frontend/panels.tsx +17 -0
  123. package/templates/project-tee-starter/src/frontend/utils.ts +6 -0
  124. package/templates/project-tee-starter/src/index.ts +6 -6
  125. package/templates/project-tee-starter/src/plugin.ts +209 -59
  126. package/templates/project-tee-starter/tailwind.config.js +62 -0
  127. package/templates/project-tee-starter/tsconfig.build.json +2 -2
  128. package/templates/project-tee-starter/tsconfig.json +8 -5
  129. package/templates/project-tee-starter/tsup.config.ts +3 -2
  130. package/templates/project-tee-starter/vite.config.ts +39 -0
  131. package/dist/chunk-4O6EZU37.js +0 -14
  132. package/dist/migration-guides/advanced-migration-guide.md +0 -459
  133. package/dist/migration-guides/completion-requirements.md +0 -379
  134. package/dist/migration-guides/integrated-migration-loop.md +0 -392
  135. package/dist/migration-guides/migration-guide.md +0 -712
  136. package/dist/migration-guides/prompt-and-generation-guide.md +0 -702
  137. package/dist/migration-guides/state-and-providers-guide.md +0 -544
  138. package/dist/migration-guides/testing-guide.md +0 -1021
  139. package/dist/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  140. package/dist/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  141. package/dist/templates/plugin-starter/dist/index.js +0 -387
  142. package/dist/templates/plugin-starter/dist/index.js.map +0 -1
  143. package/templates/plugin-starter/dist/.vite/manifest.json +0 -11
  144. package/templates/plugin-starter/dist/assets/index-CgkejLs_.css +0 -1
  145. package/templates/plugin-starter/dist/assets/index-D1cHX53P.js +0 -49
  146. package/templates/plugin-starter/dist/index.d.ts +0 -14
  147. package/templates/plugin-starter/dist/index.js +0 -387
  148. 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.