Skip to content

Commit

Permalink
Merge pull request #2404 from ai16z-demirix/test/client-slack
Browse files Browse the repository at this point in the history
feat: adding tests for slack client. Moving existing tests to new __tests__ directory.
  • Loading branch information
shakkernerd authored Jan 16, 2025
2 parents 5ae8feb + 9f8f0dc commit 1000dd6
Show file tree
Hide file tree
Showing 8 changed files with 313 additions and 417 deletions.
89 changes: 89 additions & 0 deletions packages/client-slack/__tests__/message-manager.test.ts
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 packages/client-slack/__tests__/slack-client.provider.test.ts
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);
});
});
});
55 changes: 55 additions & 0 deletions packages/client-slack/__tests__/slack-client.test.ts
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');
});
});
12 changes: 5 additions & 7 deletions packages/client-slack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
],
"scripts": {
"build": "tsup src/index.ts --format esm --dts",
"test": "jest",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint --fix --cache .",
"clean": "rimraf dist",
"dev": "tsup src/index.ts --watch",
Expand All @@ -44,14 +45,11 @@
"devDependencies": {
"@types/express": "^4.17.21",
"@types/fluent-ffmpeg": "^2.1.24",
"@types/jest": "^29.5.0",
"@types/node": "^18.15.11",
"jest": "^29.5.0",
"rimraf": "^5.0.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"tsup": "^8.3.5",
"typescript": "^5.0.0"
"tsup": "^6.7.0",
"typescript": "^5.0.3",
"vitest": "^1.2.1"
},
"engines": {
"node": ">=14.0.0"
Expand Down
Loading

0 comments on commit 1000dd6

Please sign in to comment.