UNPKG

@coinbase/agentkit

Version:

Coinbase AgentKit core primitives

281 lines (280 loc) 12.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const twitter_api_v2_1 = require("twitter-api-v2"); const twitterActionProvider_1 = require("./twitterActionProvider"); const MOCK_CONFIG = { apiKey: "test-api-key", apiSecret: "test-api-secret", accessToken: "test-access-token", accessTokenSecret: "test-access-token-secret", }; const MOCK_ID = "1853889445319331840"; const MOCK_NAME = "CDP Agentkit"; const MOCK_USERNAME = "CDPAgentkit"; const MOCK_TWEET = "Hello, world!"; const MOCK_TWEET_ID = "0123456789012345678"; const MOCK_TWEET_REPLY = "Hello again!"; const MOCK_MEDIA_ID = "987654321"; const MOCK_FILE_PATH = "/path/to/image.jpg"; describe("TwitterActionProvider", () => { let mockClient; let provider; let mockUploadMedia; beforeEach(() => { mockClient = { me: jest.fn(), userMentionTimeline: jest.fn(), tweet: jest.fn(), }; mockUploadMedia = jest.fn(); jest.spyOn(twitter_api_v2_1.TwitterApi.prototype, "v2", "get").mockReturnValue(mockClient); // eslint-disable-next-line @typescript-eslint/no-explicit-any jest.spyOn(twitter_api_v2_1.TwitterApi.prototype, "v1", "get").mockReturnValue({ uploadMedia: mockUploadMedia, }); provider = new twitterActionProvider_1.TwitterActionProvider(MOCK_CONFIG); }); describe("Constructor", () => { it("should initialize with config values", () => { expect(() => new twitterActionProvider_1.TwitterActionProvider(MOCK_CONFIG)).not.toThrow(); }); it("should initialize with environment variables", () => { process.env.TWITTER_API_KEY = MOCK_CONFIG.apiKey; process.env.TWITTER_API_SECRET = MOCK_CONFIG.apiSecret; process.env.TWITTER_ACCESS_TOKEN = MOCK_CONFIG.accessToken; process.env.TWITTER_ACCESS_TOKEN_SECRET = MOCK_CONFIG.accessTokenSecret; expect(() => new twitterActionProvider_1.TwitterActionProvider()).not.toThrow(); }); it("should throw error if no config or env vars", () => { delete process.env.TWITTER_API_KEY; delete process.env.TWITTER_API_SECRET; delete process.env.TWITTER_ACCESS_TOKEN; delete process.env.TWITTER_ACCESS_TOKEN_SECRET; expect(() => new twitterActionProvider_1.TwitterActionProvider()).toThrow("TWITTER_API_KEY is not configured."); }); it("should implement lazy initialization", async () => { // We'll check lazy initialization by observing that we can create an instance // without error, and then use it to call methods successfully const provider = new twitterActionProvider_1.TwitterActionProvider(MOCK_CONFIG); // Mock a response for accountDetails mockClient.me.mockResolvedValue({ data: { id: MOCK_ID, name: MOCK_NAME, username: MOCK_USERNAME, }, }); // Call a method that should trigger initialization const response = await provider.accountDetails({}); // Verify the method worked, which means initialization succeeded expect(response).toContain("Successfully retrieved authenticated user account details"); expect(mockClient.me).toHaveBeenCalled(); }); }); describe("Account Details Action", () => { const mockResponse = { data: { id: MOCK_ID, name: MOCK_NAME, username: MOCK_USERNAME, }, }; beforeEach(() => { mockClient.me.mockResolvedValue(mockResponse); }); it("should successfully retrieve account details", async () => { const response = await provider.accountDetails({}); expect(mockClient.me).toHaveBeenCalled(); expect(response).toContain("Successfully retrieved authenticated user account details"); expect(response).toContain(JSON.stringify({ ...mockResponse, data: { ...mockResponse.data, url: `https://x.com/${MOCK_USERNAME}` }, })); }); it("should handle errors when retrieving account details", async () => { const error = new Error("An error has occurred"); mockClient.me.mockRejectedValue(error); const response = await provider.accountDetails({}); expect(mockClient.me).toHaveBeenCalled(); expect(response).toContain("Error retrieving authenticated user account details"); expect(response).toContain(error.message); }); }); describe("Account Mentions Action", () => { const mockResponse = { _realData: { data: [ { id: MOCK_TWEET_ID, text: "@CDPAgentkit please reply!", }, ], }, data: [ { id: MOCK_TWEET_ID, text: "@CDPAgentkit please reply!", }, ], meta: {}, _endpoint: {}, tweets: [], getItemArray: () => [], refreshInstanceFromResult: () => mockResponse, }; beforeEach(() => { mockClient.userMentionTimeline.mockResolvedValue(mockResponse); }); it("should successfully retrieve account mentions", async () => { const response = await provider.accountMentions({ userId: MOCK_ID }); expect(mockClient.userMentionTimeline).toHaveBeenCalledWith(MOCK_ID); expect(response).toContain("Successfully retrieved account mentions"); expect(response).toContain(JSON.stringify(mockResponse)); }); it("should handle errors when retrieving mentions", async () => { const error = new Error("An error has occurred"); mockClient.userMentionTimeline.mockRejectedValue(error); const response = await provider.accountMentions({ userId: MOCK_ID }); expect(mockClient.userMentionTimeline).toHaveBeenCalledWith(MOCK_ID); expect(response).toContain("Error retrieving authenticated account mentions"); expect(response).toContain(error.message); }); }); describe("Post Tweet Action", () => { const mockResponse = { data: { id: MOCK_TWEET_ID, text: MOCK_TWEET, edit_history_tweet_ids: [MOCK_TWEET_ID], }, }; beforeEach(() => { mockClient.tweet.mockResolvedValue(mockResponse); }); it("should successfully post a tweet", async () => { const response = await provider.postTweet({ tweet: MOCK_TWEET }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, {}); expect(response).toContain("Successfully posted to Twitter"); expect(response).toContain(JSON.stringify(mockResponse)); }); it("should successfully post a tweet with media", async () => { const mediaIds = [MOCK_MEDIA_ID]; const response = await provider.postTweet({ tweet: MOCK_TWEET, mediaIds }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, { media: { media_ids: mediaIds }, }); expect(response).toContain("Successfully posted to Twitter"); expect(response).toContain(JSON.stringify(mockResponse)); }); it("should handle errors when posting a tweet", async () => { const error = new Error("An error has occurred"); mockClient.tweet.mockRejectedValue(error); const response = await provider.postTweet({ tweet: MOCK_TWEET }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET, {}); expect(response).toContain("Error posting to Twitter"); expect(response).toContain(error.message); }); }); describe("Post Tweet Reply Action", () => { const mockResponse = { data: { id: MOCK_TWEET_ID, text: MOCK_TWEET_REPLY, edit_history_tweet_ids: [MOCK_TWEET_ID], }, }; beforeEach(() => { mockClient.tweet.mockResolvedValue(mockResponse); }); it("should successfully post a tweet reply", async () => { const response = await provider.postTweetReply({ tweetId: MOCK_TWEET_ID, tweetReply: MOCK_TWEET_REPLY, }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET_REPLY, { reply: { in_reply_to_tweet_id: MOCK_TWEET_ID }, }); expect(response).toContain("Successfully posted reply to Twitter"); expect(response).toContain(JSON.stringify(mockResponse)); }); it("should successfully post a tweet reply with media", async () => { const mediaIds = [MOCK_MEDIA_ID]; const response = await provider.postTweetReply({ tweetId: MOCK_TWEET_ID, tweetReply: MOCK_TWEET_REPLY, mediaIds, }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET_REPLY, { reply: { in_reply_to_tweet_id: MOCK_TWEET_ID }, media: { media_ids: mediaIds }, }); expect(response).toContain("Successfully posted reply to Twitter"); expect(response).toContain(JSON.stringify(mockResponse)); }); it("should handle errors when posting a tweet reply", async () => { const error = new Error("An error has occurred"); mockClient.tweet.mockRejectedValue(error); const response = await provider.postTweetReply({ tweetId: MOCK_TWEET_ID, tweetReply: MOCK_TWEET_REPLY, }); expect(mockClient.tweet).toHaveBeenCalledWith(MOCK_TWEET_REPLY, { reply: { in_reply_to_tweet_id: MOCK_TWEET_ID }, }); expect(response).toContain("Error posting reply to Twitter"); expect(response).toContain(error.message); }); }); describe("Upload Media Action", () => { beforeEach(() => { mockUploadMedia.mockResolvedValue(MOCK_MEDIA_ID); }); it("should successfully upload media", async () => { const response = await provider.uploadMedia({ filePath: MOCK_FILE_PATH }); expect(mockUploadMedia).toHaveBeenCalledWith(MOCK_FILE_PATH); expect(response).toContain("Successfully uploaded media to Twitter"); expect(response).toContain(MOCK_MEDIA_ID); }); it("should handle errors when uploading media", async () => { const error = new Error("Invalid file format"); mockUploadMedia.mockRejectedValue(error); const response = await provider.uploadMedia({ filePath: MOCK_FILE_PATH }); expect(mockUploadMedia).toHaveBeenCalledWith(MOCK_FILE_PATH); expect(response).toContain("Error uploading media to Twitter"); expect(response).toContain(error.message); }); }); describe("Network Support", () => { it("should always return true for network support", () => { expect(provider.supportsNetwork({ protocolFamily: "evm", networkId: "1" })).toBe(true); expect(provider.supportsNetwork({ protocolFamily: "solana", networkId: "2" })).toBe(true); }); }); describe("Next.js Integration", () => { it("should work in a simulated Next.js environment", async () => { // Create a clean instance of the provider const provider = new twitterActionProvider_1.TwitterActionProvider(MOCK_CONFIG); // Mock v2.me to test functionality const mockResponse = { data: { id: MOCK_ID, name: MOCK_NAME, username: MOCK_USERNAME, }, }; const mockTwitterApi = { v2: { me: jest.fn().mockResolvedValue(mockResponse), }, }; // Override the getClient method to return our mocked API jest .spyOn(provider, "getClient") .mockReturnValue(mockTwitterApi); // Simulate a Next.js API route calling the provider const result = await provider.accountDetails({}); expect(result).toContain("Successfully retrieved authenticated user account details"); expect(mockTwitterApi.v2.me).toHaveBeenCalled(); }); }); });