Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: adding tests for twitter-client #1959

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions packages/client-twitter/__tests__/base.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { ClientBase } from '../src/base';
import { IAgentRuntime } from '@elizaos/core';
import { TwitterConfig } from '../src/environment';

describe('Twitter Client Base', () => {
let mockRuntime: IAgentRuntime;
let mockConfig: TwitterConfig;

beforeEach(() => {
mockRuntime = {
env: {
TWITTER_USERNAME: 'testuser',
TWITTER_DRY_RUN: 'true',
TWITTER_POST_INTERVAL_MIN: '5',
TWITTER_POST_INTERVAL_MAX: '10',
TWITTER_ACTION_INTERVAL: '5',
TWITTER_ENABLE_ACTION_PROCESSING: 'true',
TWITTER_POST_IMMEDIATELY: 'false',
TWITTER_SEARCH_ENABLE: 'false'
},
getEnv: function (key: string) {
return this.env[key] || null;
},
getSetting: function (key: string) {
return this.env[key] || null;
},
character: {
style: {
all: ['Test style 1', 'Test style 2'],
post: ['Post style 1', 'Post style 2']
}
}
} as unknown as IAgentRuntime;

mockConfig = {
TWITTER_USERNAME: 'testuser',
TWITTER_DRY_RUN: true,
TWITTER_SEARCH_ENABLE: false,
TWITTER_SPACES_ENABLE: false,
TWITTER_TARGET_USERS: [],
TWITTER_MAX_TWEETS_PER_DAY: 10,
TWITTER_MAX_TWEET_LENGTH: 280,
POST_INTERVAL_MIN: 5,
POST_INTERVAL_MAX: 10,
ACTION_INTERVAL: 5,
ENABLE_ACTION_PROCESSING: true,
POST_IMMEDIATELY: false
};
});

it('should create instance with correct configuration', () => {
const client = new ClientBase(mockRuntime, mockConfig);
expect(client).toBeDefined();
expect(client.twitterConfig).toBeDefined();
expect(client.twitterConfig.TWITTER_USERNAME).toBe('testuser');
expect(client.twitterConfig.TWITTER_DRY_RUN).toBe(true);
});

it('should initialize with correct tweet length limit', () => {
const client = new ClientBase(mockRuntime, mockConfig);
expect(client.twitterConfig.TWITTER_MAX_TWEET_LENGTH).toBe(280);
});

it('should initialize with correct post intervals', () => {
const client = new ClientBase(mockRuntime, mockConfig);
expect(client.twitterConfig.POST_INTERVAL_MIN).toBe(5);
expect(client.twitterConfig.POST_INTERVAL_MAX).toBe(10);
});

it('should initialize with correct action settings', () => {
const client = new ClientBase(mockRuntime, mockConfig);
expect(client.twitterConfig.ACTION_INTERVAL).toBe(5);
expect(client.twitterConfig.ENABLE_ACTION_PROCESSING).toBe(true);
});
});
134 changes: 134 additions & 0 deletions packages/client-twitter/__tests__/environment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { describe, it, expect, vi } from 'vitest';
import { validateTwitterConfig } from '../src/environment';
import { IAgentRuntime } from '@elizaos/core';

describe('Twitter Environment Configuration', () => {
const mockRuntime: IAgentRuntime = {
env: {
TWITTER_USERNAME: 'testuser123',
TWITTER_DRY_RUN: 'true',
TWITTER_SEARCH_ENABLE: 'false',
TWITTER_SPACES_ENABLE: 'false',
TWITTER_TARGET_USERS: 'user1,user2,user3',
TWITTER_MAX_TWEETS_PER_DAY: '10',
TWITTER_MAX_TWEET_LENGTH: '280',
TWITTER_POST_INTERVAL_MIN: '90',
TWITTER_POST_INTERVAL_MAX: '180',
TWITTER_ACTION_INTERVAL: '5',
TWITTER_ENABLE_ACTION_PROCESSING: 'false',
TWITTER_POST_IMMEDIATELY: 'false',
TWITTER_EMAIL: '[email protected]',
TWITTER_PASSWORD: 'hashedpassword',
TWITTER_2FA_SECRET: '',
TWITTER_POLL_INTERVAL: '120',
TWITTER_RETRY_LIMIT: '5',
ACTION_TIMELINE_TYPE: 'foryou',
MAX_ACTIONS_PROCESSING: '1',
MAX_TWEET_LENGTH: '280'
},
getEnv: function (key: string) {
return this.env[key] || null;
},
getSetting: function (key: string) {
return this.env[key] || null;
}
} as unknown as IAgentRuntime;

it('should validate correct configuration', async () => {
const config = await validateTwitterConfig(mockRuntime);
expect(config).toBeDefined();
expect(config.TWITTER_USERNAME).toBe('testuser123');
expect(config.TWITTER_DRY_RUN).toBe(true);
expect(config.TWITTER_SEARCH_ENABLE).toBe(false);
expect(config.TWITTER_SPACES_ENABLE).toBe(false);
expect(config.TWITTER_TARGET_USERS).toEqual(['user1', 'user2', 'user3']);
expect(config.MAX_TWEET_LENGTH).toBe(280);
expect(config.POST_INTERVAL_MIN).toBe(90);
expect(config.POST_INTERVAL_MAX).toBe(180);
expect(config.ACTION_INTERVAL).toBe(5);
expect(config.ENABLE_ACTION_PROCESSING).toBe(false);
expect(config.POST_IMMEDIATELY).toBe(false);
});

it('should validate wildcard username', async () => {
const wildcardRuntime = {
...mockRuntime,
env: {
...mockRuntime.env,
TWITTER_USERNAME: '*'
},
getEnv: function(key: string) {
return this.env[key] || null;
},
getSetting: function(key: string) {
return this.env[key] || null;
}
} as IAgentRuntime;

const config = await validateTwitterConfig(wildcardRuntime);
expect(config.TWITTER_USERNAME).toBe('*');
});

it('should validate username with numbers and underscores', async () => {
const validRuntime = {
...mockRuntime,
env: {
...mockRuntime.env,
TWITTER_USERNAME: 'test_user_123'
},
getEnv: function(key: string) {
return this.env[key] || null;
},
getSetting: function(key: string) {
return this.env[key] || null;
}
} as IAgentRuntime;

const config = await validateTwitterConfig(validRuntime);
expect(config.TWITTER_USERNAME).toBe('test_user_123');
});

it('should handle empty target users', async () => {
const runtimeWithoutTargets = {
...mockRuntime,
env: {
...mockRuntime.env,
TWITTER_TARGET_USERS: ''
},
getEnv: function(key: string) {
return this.env[key] || null;
},
getSetting: function(key: string) {
return this.env[key] || null;
}
} as IAgentRuntime;

const config = await validateTwitterConfig(runtimeWithoutTargets);
expect(config.TWITTER_TARGET_USERS).toHaveLength(0);
});

it('should use default values when optional configs are missing', async () => {
const minimalRuntime = {
env: {
TWITTER_USERNAME: 'testuser',
TWITTER_DRY_RUN: 'true',
TWITTER_EMAIL: '[email protected]',
TWITTER_PASSWORD: 'hashedpassword',
TWITTER_2FA_SECRET: '',
MAX_TWEET_LENGTH: '280'
},
getEnv: function (key: string) {
return this.env[key] || null;
},
getSetting: function (key: string) {
return this.env[key] || null;
}
} as unknown as IAgentRuntime;

const config = await validateTwitterConfig(minimalRuntime);
expect(config).toBeDefined();
expect(config.MAX_TWEET_LENGTH).toBe(280);
expect(config.POST_INTERVAL_MIN).toBe(90);
expect(config.POST_INTERVAL_MAX).toBe(180);
});
});
97 changes: 97 additions & 0 deletions packages/client-twitter/__tests__/post.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { describe, it, expect, vi } from 'vitest';
import { TwitterPostClient } from '../src/post';
import { ClientBase } from '../src/base';
import { IAgentRuntime } from '@elizaos/core';
import { TwitterConfig } from '../src/environment';

describe('Twitter Post Client', () => {
let mockRuntime: IAgentRuntime;
let mockConfig: TwitterConfig;
let baseClient: ClientBase;

beforeEach(() => {
mockRuntime = {
env: {
TWITTER_USERNAME: 'testuser',
TWITTER_DRY_RUN: 'true',
TWITTER_POST_INTERVAL_MIN: '5',
TWITTER_POST_INTERVAL_MAX: '10',
TWITTER_ACTION_INTERVAL: '5',
TWITTER_ENABLE_ACTION_PROCESSING: 'true',
TWITTER_POST_IMMEDIATELY: 'false',
TWITTER_SEARCH_ENABLE: 'false',
TWITTER_EMAIL: '[email protected]',
TWITTER_PASSWORD: 'hashedpassword',
TWITTER_2FA_SECRET: '',
TWITTER_POLL_INTERVAL: '120',
TWITTER_RETRY_LIMIT: '5',
ACTION_TIMELINE_TYPE: 'foryou',
MAX_ACTIONS_PROCESSING: '1',
MAX_TWEET_LENGTH: '280'
},
getEnv: function (key: string) {
return this.env[key] || null;
},
getSetting: function (key: string) {
return this.env[key] || null;
},
character: {
style: {
all: ['Test style 1', 'Test style 2'],
post: ['Post style 1', 'Post style 2']
}
}
} as unknown as IAgentRuntime;

mockConfig = {
TWITTER_USERNAME: 'testuser',
TWITTER_DRY_RUN: true,
TWITTER_SEARCH_ENABLE: false,
TWITTER_SPACES_ENABLE: false,
TWITTER_TARGET_USERS: [],
TWITTER_MAX_TWEETS_PER_DAY: 10,
TWITTER_MAX_TWEET_LENGTH: 280,
POST_INTERVAL_MIN: 5,
POST_INTERVAL_MAX: 10,
ACTION_INTERVAL: 5,
ENABLE_ACTION_PROCESSING: true,
POST_IMMEDIATELY: false,
MAX_TWEET_LENGTH: 280
};

baseClient = new ClientBase(mockRuntime, mockConfig);
});

it('should create post client instance', () => {
const postClient = new TwitterPostClient(baseClient, mockRuntime);
expect(postClient).toBeDefined();
expect(postClient.twitterUsername).toBe('testuser');
expect(postClient['isDryRun']).toBe(true);
});

it('should keep tweets under max length when already valid', () => {
const postClient = new TwitterPostClient(baseClient, mockRuntime);
const validTweet = 'This is a valid tweet';
const result = postClient['trimTweetLength'](validTweet);
expect(result).toBe(validTweet);
expect(result.length).toBeLessThanOrEqual(280);
});

it('should cut at last sentence when possible', () => {
const postClient = new TwitterPostClient(baseClient, mockRuntime);
const longTweet = 'First sentence. Second sentence that is quite long. Third sentence that would make it too long.';
const result = postClient['trimTweetLength'](longTweet);
const lastPeriod = result.lastIndexOf('.');
expect(lastPeriod).toBeGreaterThan(0);
expect(result.length).toBeLessThanOrEqual(280);
});

it('should add ellipsis when cutting within a sentence', () => {
const postClient = new TwitterPostClient(baseClient, mockRuntime);
const longSentence = 'This is an extremely long sentence without any periods that needs to be truncated because it exceeds the maximum allowed length for a tweet on the Twitter platform and therefore must be shortened';
const result = postClient['trimTweetLength'](longSentence);
const lastSpace = result.lastIndexOf(' ');
expect(lastSpace).toBeGreaterThan(0);
expect(result.length).toBeLessThanOrEqual(280);
});
});
8 changes: 6 additions & 2 deletions packages/client-twitter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,16 @@
"zod": "3.23.8"
},
"devDependencies": {
"tsup": "8.3.5"
"tsup": "8.3.5",
"vitest": "1.1.3",
"@vitest/coverage-v8": "1.1.3"
},
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint --fix --cache ."
"lint": "eslint --fix --cache .",
"test": "vitest run",
"test:coverage": "vitest run --coverage"
},
"peerDependencies": {
"whatwg-url": "7.1.0"
Expand Down
12 changes: 12 additions & 0 deletions packages/client-twitter/vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from 'vitest/config';

export default defineConfig({
test: {
globals: true,
environment: 'node',
include: ['__tests__/**/*.test.ts'],
coverage: {
reporter: ['text', 'json', 'html'],
},
},
});
Loading