From 920f1a05df2cb6bfb26b87fdaa6ccca57b729159 Mon Sep 17 00:00:00 2001 From: Shaw Date: Mon, 10 Feb 2025 16:56:25 -0500 Subject: [PATCH] update test runner --- package.json | 2 +- packages/agent/src/plugins.test.ts | 98 ++++++++++++++++++++++++++-- packages/core/src/types.ts | 3 +- packages/plugin-discord/src/index.ts | 17 +++++ 4 files changed, 112 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 8e97dc12ce3..1a2998276ab 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "docker:bash": "bash ./scripts/docker.sh bash", "docker:start": "bash ./scripts/docker.sh start", "docker": "bun docker:build && bun docker:run && bun docker:bash", - "test-e2e": "turbo run test --filter=@elizaos/agent" + "test": "turbo run test --filter=@elizaos/agent" }, "devDependencies": { "@biomejs/biome": "^1.9.4", diff --git a/packages/agent/src/plugins.test.ts b/packages/agent/src/plugins.test.ts index 22c4e9c7c73..460d1bbb086 100644 --- a/packages/agent/src/plugins.test.ts +++ b/packages/agent/src/plugins.test.ts @@ -1,6 +1,7 @@ import dotenv from 'dotenv'; dotenv.config({ path: '../../.env' }); +import type { IDatabaseAdapter, IDatabaseCacheAdapter, TestCase } from "@elizaos/core"; import { AgentRuntime, CacheManager, @@ -8,12 +9,11 @@ import { logger, stringToUuid, TestSuite, - type IAgentRuntime, - type IDatabaseAdapter, - type IDatabaseCacheAdapter + type IAgentRuntime } from "@elizaos/core"; import { afterAll, beforeAll, describe, it } from 'vitest'; -import { defaultCharacter } from "./defaultCharacter"; +import { defaultCharacter } from './defaultCharacter'; + let runtime: IAgentRuntime; let db: IDatabaseAdapter & IDatabaseCacheAdapter; @@ -121,7 +121,7 @@ describe('Plugin Tests', async () => { const startTime = performance.now(); try { - await test.fn(); + await test.fn(runtime); stats.passed++; const duration = performance.now() - startTime; logger.info(`✓ ${test.name} (${Math.round(duration)}ms)`); @@ -146,4 +146,92 @@ describe('Plugin Tests', async () => { logger.info(`Failed: ${stats.failed}`); logger.info(`Skipped: ${stats.skipped}`); }); +}); +interface TestStats { + total: number; + passed: number; + failed: number; + skipped: number; +} + +class TestRunner { + private runtime: IAgentRuntime; + private stats: TestStats; + + constructor(runtime: IAgentRuntime) { + this.runtime = runtime; + this.stats = { + total: 0, + passed: 0, + failed: 0, + skipped: 0 + }; + } + + private async runTestCase(test: TestCase): Promise { + const startTime = performance.now(); + try { + await test.fn(this.runtime); + this.stats.passed++; + const duration = performance.now() - startTime; + logger.info(`✓ ${test.name} (${Math.round(duration)}ms)`); + } catch (error) { + this.stats.failed++; + logger.error(`✗ ${test.name}`); + logger.error(error); + throw error; + } + } + + private async runTestSuite(suite: TestSuite): Promise { + logger.info(`\nTest suite: ${suite.name}`); + for (const test of suite.tests) { + this.stats.total++; + await this.runTestCase(test); + } + } + + public async runPluginTests(): Promise { + const plugins = this.runtime.plugins; + + for (const plugin of plugins) { + if (!plugin.tests) { + logger.info(`Plugin ${plugin.name} has no tests`); + continue; + } + + try { + logger.info(`Running tests for plugin: ${plugin.name}`); + const pluginTests = plugin.tests; + // Handle both single suite and array of suites + const testSuites = Array.isArray(pluginTests) ? pluginTests : [pluginTests]; + + for (const suite of testSuites) { + await this.runTestSuite(suite); + } + } catch (error) { + logger.error(`Error in plugin ${plugin.name}:`, error); + throw error; + } + } + + this.logTestSummary(); + return this.stats; + } + + private logTestSummary(): void { + logger.info('\nTest Summary:'); + logger.info(`Total: ${this.stats.total}`); + logger.info(`Passed: ${this.stats.passed}`); + logger.info(`Failed: ${this.stats.failed}`); + logger.info(`Skipped: ${this.stats.skipped}`); + } +} + +// Main test suite that runs all plugin tests +describe('Plugin Tests', () => { + it('should run all plugin tests', async () => { + const testRunner = new TestRunner(runtime); + await testRunner.runPluginTests(); + }); }); \ No newline at end of file diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index ac2433d2f21..243f8908f34 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -1105,10 +1105,9 @@ export interface IFileService extends Service { generateSignedUrl(fileName: string, expiresIn: number): Promise; } -// types.ts or in core package export interface TestCase { name: string; - fn: () => Promise | void; + fn: (runtime: IAgentRuntime) => Promise | void; } export interface TestSuite { diff --git a/packages/plugin-discord/src/index.ts b/packages/plugin-discord/src/index.ts index 54c2b39a764..533ed7fe405 100644 --- a/packages/plugin-discord/src/index.ts +++ b/packages/plugin-discord/src/index.ts @@ -1,6 +1,7 @@ import { logger, stringToUuid, + TestSuite, type Character, type Client as ElizaClient, type IAgentRuntime, @@ -391,6 +392,19 @@ const DiscordClientInterface: ElizaClient = { start: async (runtime: IAgentRuntime) => new DiscordClient(runtime), }; +const testSuite: TestSuite = { + name: "discord", + tests: [ + { + name: "discord", + fn: async (runtime: IAgentRuntime) => { + const discordClient = new DiscordClient(runtime); + console.log("Created a discord client"); + } + } + ] +}; + const discordPlugin: Plugin = { name: "discord", description: "Discord client plugin", @@ -407,6 +421,9 @@ const discordPlugin: Plugin = { providers: [ channelStateProvider, voiceStateProvider, + ], + tests: [ + testSuite, ] }; export default discordPlugin; \ No newline at end of file