-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2404 from ai16z-demirix/test/client-slack
feat: adding tests for slack client. Moving existing tests to new __tests__ directory.
- Loading branch information
Showing
8 changed files
with
313 additions
and
417 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
import { MessageManager } from '../src/messages'; | ||
import { WebClient } from '@slack/web-api'; | ||
import { IAgentRuntime } from '@elizaos/core'; | ||
|
||
// Mock dependencies | ||
vi.mock('@slack/web-api'); | ||
vi.mock('@elizaos/core'); | ||
|
||
describe('MessageManager', () => { | ||
let mockWebClient: WebClient; | ||
let mockRuntime: IAgentRuntime; | ||
let messageManager: MessageManager; | ||
const mockBotUserId = 'U123456'; | ||
|
||
beforeEach(() => { | ||
// Setup mock WebClient | ||
mockWebClient = { | ||
chat: { | ||
postMessage: vi.fn() | ||
} | ||
} as unknown as WebClient; | ||
|
||
// Setup mock runtime | ||
mockRuntime = { | ||
getSetting: vi.fn(), | ||
character: { | ||
name: 'TestBot' | ||
} | ||
} as unknown as IAgentRuntime; | ||
|
||
messageManager = new MessageManager(mockWebClient, mockRuntime, mockBotUserId); | ||
}); | ||
|
||
it('should initialize with correct parameters', () => { | ||
expect(messageManager).toBeDefined(); | ||
}); | ||
|
||
it('should not process duplicate events', () => { | ||
const eventId = 'evt_123'; | ||
const result1 = messageManager['processedEvents'].has(eventId); | ||
expect(result1).toBe(false); | ||
|
||
// Add event to processed set | ||
messageManager['processedEvents'].add(eventId); | ||
const result2 = messageManager['processedEvents'].has(eventId); | ||
expect(result2).toBe(true); | ||
}); | ||
|
||
it('should handle message processing lock correctly', () => { | ||
const messageId = 'msg_123'; | ||
const isLocked1 = messageManager['messageProcessingLock'].has(messageId); | ||
expect(isLocked1).toBe(false); | ||
|
||
// Lock message | ||
messageManager['messageProcessingLock'].add(messageId); | ||
const isLocked2 = messageManager['messageProcessingLock'].has(messageId); | ||
expect(isLocked2).toBe(true); | ||
}); | ||
|
||
it('should clean up old processed messages', () => { | ||
vi.useFakeTimers(); | ||
const oldMessageId = 'old_msg'; | ||
const newMessageId = 'new_msg'; | ||
|
||
// Add messages with different timestamps | ||
messageManager['processedMessages'].set(oldMessageId, Date.now() - 3700000); // older than 1 hour | ||
messageManager['processedMessages'].set(newMessageId, Date.now()); // current | ||
|
||
// Trigger cleanup by advancing time and running interval callback | ||
const cleanupInterval = setInterval(() => { | ||
const oneHourAgo = Date.now() - 3600000; | ||
for (const [key, timestamp] of messageManager['processedMessages'].entries()) { | ||
if (timestamp < oneHourAgo) { | ||
messageManager['processedMessages'].delete(key); | ||
} | ||
} | ||
}, 3600000); | ||
|
||
vi.advanceTimersByTime(3600000); | ||
|
||
// Check if old message was cleaned up | ||
expect(messageManager['processedMessages'].has(oldMessageId)).toBe(false); | ||
expect(messageManager['processedMessages'].has(newMessageId)).toBe(true); | ||
|
||
clearInterval(cleanupInterval); | ||
vi.useRealTimers(); | ||
}); | ||
}); |
151 changes: 151 additions & 0 deletions
151
packages/client-slack/__tests__/slack-client.provider.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import { describe, expect, test, beforeEach, vi } from "vitest"; | ||
import { SlackClientProvider } from "../src/providers/slack-client.provider"; | ||
import { SlackConfig } from "../src/types/slack-types"; | ||
import { WebClient } from "@slack/web-api"; | ||
import type { | ||
AuthTestResponse, | ||
ChatPostMessageResponse, | ||
} from "@slack/web-api"; | ||
|
||
vi.mock("@slack/web-api"); | ||
|
||
// Mock setup functions | ||
const createMockSlackResponse = (ok: boolean, additionalData = {}) => ({ | ||
ok, | ||
...additionalData, | ||
}); | ||
|
||
const getMockWebClient = () => { | ||
return { | ||
auth: { | ||
test: vi.fn(), | ||
}, | ||
chat: { | ||
postMessage: vi.fn(), | ||
}, | ||
} as unknown as WebClient; | ||
}; | ||
|
||
describe("SlackClientProvider", () => { | ||
let provider: SlackClientProvider; | ||
let mockWebClient: WebClient; | ||
let mockConfig: SlackConfig; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
mockConfig = { | ||
appId: "test-app-id", | ||
clientId: "test-client-id", | ||
clientSecret: "test-client-secret", | ||
signingSecret: "test-signing-secret", | ||
verificationToken: "test-verification-token", | ||
botToken: "test-bot-token", | ||
botId: "test-bot-id", | ||
}; | ||
mockWebClient = getMockWebClient(); | ||
provider = new SlackClientProvider(mockConfig); | ||
// @ts-ignore - setting mock client for testing | ||
provider['client'] = mockWebClient; | ||
}); | ||
|
||
describe("Initialization", () => { | ||
test("should create a provider instance with default retry options", () => { | ||
expect(provider).toBeInstanceOf(SlackClientProvider); | ||
const context = provider.getContext(); | ||
expect(context).toHaveProperty("client"); | ||
expect(context).toHaveProperty("config"); | ||
expect(context.config).toEqual(mockConfig); | ||
}); | ||
|
||
test("should create a provider instance with custom retry options", () => { | ||
const retryOptions = { | ||
maxRetries: 5, | ||
initialDelay: 2000, | ||
maxDelay: 10000, | ||
}; | ||
const providerWithOptions = new SlackClientProvider(mockConfig, retryOptions); | ||
// @ts-ignore - setting mock client for testing | ||
providerWithOptions['client'] = mockWebClient; | ||
|
||
expect(providerWithOptions).toBeInstanceOf(SlackClientProvider); | ||
const context = providerWithOptions.getContext(); | ||
expect(context).toHaveProperty("client"); | ||
expect(context).toHaveProperty("config"); | ||
expect(context.config).toEqual(mockConfig); | ||
}); | ||
}); | ||
|
||
describe("Connection Validation", () => { | ||
test("should validate connection successfully", async () => { | ||
const mockResponse = createMockSlackResponse(true, { | ||
user_id: "test-bot-id", | ||
}) as AuthTestResponse; | ||
const mockTest = mockWebClient.auth.test as vi.Mock; | ||
mockTest.mockResolvedValue(mockResponse); | ||
|
||
const result = await provider.validateConnection(); | ||
expect(result).toBe(true); | ||
}); | ||
|
||
test("should handle failed validation", async () => { | ||
const mockResponse = createMockSlackResponse(false) as AuthTestResponse; | ||
const mockTest = mockWebClient.auth.test as vi.Mock; | ||
mockTest.mockResolvedValue(mockResponse); | ||
|
||
const result = await provider.validateConnection(); | ||
expect(result).toBe(false); | ||
}); | ||
|
||
test("should handle connection errors", async () => { | ||
const mockTest = mockWebClient.auth.test as vi.Mock; | ||
mockTest.mockRejectedValue(new Error("Connection failed")); | ||
|
||
const result = await provider.validateConnection(); | ||
expect(result).toBe(false); | ||
}); | ||
}); | ||
|
||
describe("Message Sending", () => { | ||
const channelId = "test-channel"; | ||
const text = "Hello, world!"; | ||
|
||
test("should successfully send a message", async () => { | ||
const expectedResponse = createMockSlackResponse(true, { | ||
ts: "1234567890.123456", | ||
}) as ChatPostMessageResponse; | ||
const mockPostMessage = mockWebClient.chat.postMessage as vi.Mock; | ||
mockPostMessage.mockResolvedValue(expectedResponse); | ||
|
||
const result = await provider.sendMessage(channelId, text); | ||
expect(result.ok).toBe(true); | ||
expect(mockPostMessage).toHaveBeenCalledWith({ | ||
channel: channelId, | ||
text: text, | ||
}); | ||
}); | ||
|
||
test("should handle rate limiting", async () => { | ||
const mockResponse = createMockSlackResponse(true) as ChatPostMessageResponse; | ||
const mockPostMessage = mockWebClient.chat.postMessage as vi.Mock; | ||
|
||
mockPostMessage | ||
.mockRejectedValueOnce(new Error("rate_limited")) | ||
.mockResolvedValueOnce(mockResponse); | ||
|
||
const result = await provider.sendMessage(channelId, text); | ||
expect(result.ok).toBe(true); | ||
}); | ||
|
||
test("should handle network errors with retry", async () => { | ||
const mockResponse = createMockSlackResponse(true) as ChatPostMessageResponse; | ||
const mockPostMessage = mockWebClient.chat.postMessage as vi.Mock; | ||
|
||
mockPostMessage | ||
.mockRejectedValueOnce(new Error("network_error")) | ||
.mockResolvedValueOnce(mockResponse); | ||
|
||
const result = await provider.sendMessage(channelId, text); | ||
expect(result.ok).toBe(true); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
import { SlackClient } from '../src/index'; | ||
import { WebClient } from '@slack/web-api'; | ||
import { IAgentRuntime, Character } from '@elizaos/core'; | ||
|
||
// Mock dependencies | ||
vi.mock('@slack/web-api'); | ||
vi.mock('@elizaos/core'); | ||
|
||
describe('SlackClient', () => { | ||
let mockRuntime: IAgentRuntime; | ||
let slackClient: SlackClient; | ||
|
||
beforeEach(() => { | ||
// Setup mock runtime | ||
mockRuntime = { | ||
getSetting: vi.fn((key: string) => { | ||
const settings: { [key: string]: string } = { | ||
'SLACK_BOT_TOKEN': 'test-token', | ||
'SLACK_SIGNING_SECRET': 'test-secret' | ||
}; | ||
return settings[key]; | ||
}), | ||
character: {} as Character | ||
} as unknown as IAgentRuntime; | ||
}); | ||
|
||
it('should initialize with correct settings', () => { | ||
slackClient = new SlackClient(mockRuntime); | ||
expect(mockRuntime.getSetting).toHaveBeenCalledWith('SLACK_BOT_TOKEN'); | ||
expect(mockRuntime.getSetting).toHaveBeenCalledWith('SLACK_SIGNING_SECRET'); | ||
}); | ||
|
||
it('should throw error if SLACK_BOT_TOKEN is missing', () => { | ||
mockRuntime.getSetting = vi.fn((key: string) => { | ||
const settings: { [key: string]: string } = { | ||
'SLACK_SIGNING_SECRET': 'test-secret' | ||
}; | ||
return settings[key]; | ||
}); | ||
|
||
expect(() => new SlackClient(mockRuntime)).toThrow('SLACK_BOT_TOKEN is required'); | ||
}); | ||
|
||
it('should throw error if SLACK_SIGNING_SECRET is missing', () => { | ||
mockRuntime.getSetting = vi.fn((key: string) => { | ||
const settings: { [key: string]: string } = { | ||
'SLACK_BOT_TOKEN': 'test-token' | ||
}; | ||
return settings[key]; | ||
}); | ||
|
||
expect(() => new SlackClient(mockRuntime)).toThrow('SLACK_SIGNING_SECRET is required'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.