-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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 #1959 from ai16z-demirix/test/client-twitter
test: adding tests for twitter-client
- Loading branch information
Showing
5 changed files
with
325 additions
and
2 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,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); | ||
}); | ||
}); |
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,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); | ||
}); | ||
}); |
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,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); | ||
}); | ||
}); |
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
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,12 @@ | ||
import { defineConfig } from 'vitest/config'; | ||
|
||
export default defineConfig({ | ||
test: { | ||
globals: true, | ||
environment: 'node', | ||
include: ['__tests__/**/*.test.ts'], | ||
coverage: { | ||
reporter: ['text', 'json', 'html'], | ||
}, | ||
}, | ||
}); |