Skip to content

Commit 5a85d7f

Browse files
feat: adding tests for instagram client (#2454)
Co-authored-by: Sayo <hi@sayo.wtf>
1 parent 9e0af0e commit 5a85d7f

File tree

3 files changed

+215
-2
lines changed

3 files changed

+215
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import { validateInstagramConfig, instagramEnvSchema } from '../src/environment';
3+
import { IAgentRuntime } from '@elizaos/core';
4+
5+
describe('Instagram Environment Configuration', () => {
6+
const mockRuntime: IAgentRuntime = {
7+
getSetting: vi.fn(),
8+
} as unknown as IAgentRuntime;
9+
10+
it('validates correct Instagram configuration', async () => {
11+
const validConfig = {
12+
INSTAGRAM_DRY_RUN: false,
13+
INSTAGRAM_USERNAME: 'test_user',
14+
INSTAGRAM_PASSWORD: 'test_password',
15+
INSTAGRAM_APP_ID: 'test_app_id',
16+
INSTAGRAM_APP_SECRET: 'test_app_secret',
17+
INSTAGRAM_POST_INTERVAL_MIN: 60,
18+
INSTAGRAM_POST_INTERVAL_MAX: 120,
19+
INSTAGRAM_ENABLE_ACTION_PROCESSING: false,
20+
INSTAGRAM_ACTION_INTERVAL: 5,
21+
INSTAGRAM_MAX_ACTIONS: 1,
22+
};
23+
24+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
25+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
26+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return 'false';
27+
return validConfig[key as keyof typeof validConfig];
28+
});
29+
30+
const config = await validateInstagramConfig(mockRuntime);
31+
expect(config).toEqual(validConfig);
32+
});
33+
34+
it('validates configuration with optional business account', async () => {
35+
const validConfig = {
36+
INSTAGRAM_DRY_RUN: false,
37+
INSTAGRAM_USERNAME: 'test_user',
38+
INSTAGRAM_PASSWORD: 'test_password',
39+
INSTAGRAM_APP_ID: 'test_app_id',
40+
INSTAGRAM_APP_SECRET: 'test_app_secret',
41+
INSTAGRAM_BUSINESS_ACCOUNT_ID: 'business_123',
42+
INSTAGRAM_POST_INTERVAL_MIN: 60,
43+
INSTAGRAM_POST_INTERVAL_MAX: 120,
44+
INSTAGRAM_ENABLE_ACTION_PROCESSING: false,
45+
INSTAGRAM_ACTION_INTERVAL: 5,
46+
INSTAGRAM_MAX_ACTIONS: 1,
47+
};
48+
49+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
50+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
51+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return 'false';
52+
return validConfig[key as keyof typeof validConfig];
53+
});
54+
55+
const config = await validateInstagramConfig(mockRuntime);
56+
expect(config).toEqual(validConfig);
57+
});
58+
59+
it('throws error for invalid username format', async () => {
60+
const invalidConfig = {
61+
INSTAGRAM_DRY_RUN: false,
62+
INSTAGRAM_USERNAME: 'invalid@username', // Invalid characters
63+
INSTAGRAM_PASSWORD: 'test_password',
64+
INSTAGRAM_APP_ID: 'test_app_id',
65+
INSTAGRAM_APP_SECRET: 'test_app_secret',
66+
};
67+
68+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
69+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
70+
return invalidConfig[key as keyof typeof invalidConfig];
71+
});
72+
73+
await expect(validateInstagramConfig(mockRuntime)).rejects.toThrow();
74+
});
75+
76+
it('throws error for missing required fields', async () => {
77+
const invalidConfig = {
78+
INSTAGRAM_DRY_RUN: false,
79+
INSTAGRAM_USERNAME: 'test_user',
80+
// Missing password and other required fields
81+
};
82+
83+
vi.mocked(mockRuntime.getSetting).mockImplementation((key: string) => {
84+
if (key === 'INSTAGRAM_DRY_RUN') return 'false';
85+
return invalidConfig[key as keyof typeof invalidConfig];
86+
});
87+
88+
await expect(validateInstagramConfig(mockRuntime)).rejects.toThrow();
89+
});
90+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { InstagramClientInterface } from '../src';
3+
import { IAgentRuntime, elizaLogger } from '@elizaos/core';
4+
import { InstagramInteractionService } from '../src/services/interaction';
5+
import { InstagramPostService } from '../src/services/post';
6+
7+
// Mock dependencies
8+
vi.mock('@elizaos/core', async (importOriginal) => {
9+
const actual = await importOriginal();
10+
return {
11+
...actual,
12+
elizaLogger: {
13+
log: vi.fn(),
14+
error: vi.fn(),
15+
},
16+
parseBooleanFromText: (value: string | undefined) => value === 'true',
17+
};
18+
});
19+
20+
// Mock service instances
21+
const mockPostService = {
22+
start: vi.fn().mockResolvedValue(undefined),
23+
};
24+
25+
const mockInteractionService = {
26+
start: vi.fn().mockResolvedValue(undefined),
27+
};
28+
29+
vi.mock('../src/lib/auth', () => ({
30+
initializeClient: vi.fn().mockResolvedValue({
31+
ig: {},
32+
config: {
33+
INSTAGRAM_DRY_RUN: false,
34+
INSTAGRAM_ENABLE_ACTION_PROCESSING: true,
35+
},
36+
}),
37+
}));
38+
39+
vi.mock('../src/services/post', () => ({
40+
InstagramPostService: vi.fn().mockImplementation(() => mockPostService),
41+
}));
42+
43+
vi.mock('../src/services/interaction', () => ({
44+
InstagramInteractionService: vi.fn().mockImplementation(() => mockInteractionService),
45+
}));
46+
47+
describe('InstagramClientInterface', () => {
48+
let mockRuntime: IAgentRuntime;
49+
const mockConfig = {
50+
INSTAGRAM_DRY_RUN: false,
51+
INSTAGRAM_USERNAME: 'test_user',
52+
INSTAGRAM_PASSWORD: 'test_password',
53+
INSTAGRAM_APP_ID: 'test_app_id',
54+
INSTAGRAM_APP_SECRET: 'test_app_secret',
55+
INSTAGRAM_POST_INTERVAL_MIN: 60,
56+
INSTAGRAM_POST_INTERVAL_MAX: 120,
57+
INSTAGRAM_ENABLE_ACTION_PROCESSING: true,
58+
INSTAGRAM_ACTION_INTERVAL: 5,
59+
INSTAGRAM_MAX_ACTIONS: 1,
60+
};
61+
62+
beforeEach(() => {
63+
vi.clearAllMocks();
64+
mockRuntime = {
65+
getSetting: vi.fn((key: string) => {
66+
if (key === 'INSTAGRAM_DRY_RUN' || key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') {
67+
return String(mockConfig[key as keyof typeof mockConfig]);
68+
}
69+
return mockConfig[key as keyof typeof mockConfig];
70+
}),
71+
} as unknown as IAgentRuntime;
72+
});
73+
74+
it('starts successfully with all services', async () => {
75+
const result = await InstagramClientInterface.start(mockRuntime);
76+
77+
expect(result).toBeDefined();
78+
expect(result.post).toBeDefined();
79+
expect(result.interaction).toBeDefined();
80+
expect(InstagramPostService).toHaveBeenCalled();
81+
expect(InstagramInteractionService).toHaveBeenCalled();
82+
expect(result.post.start).toHaveBeenCalled();
83+
expect(result.interaction.start).toHaveBeenCalled();
84+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client configuration validated');
85+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client initialized');
86+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram post service started');
87+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram interaction service started');
88+
});
89+
90+
it('starts in dry-run mode', async () => {
91+
const dryRunConfig = { ...mockConfig, INSTAGRAM_DRY_RUN: true };
92+
mockRuntime.getSetting = vi.fn((key: string) => {
93+
if (key === 'INSTAGRAM_DRY_RUN') return 'true';
94+
if (key === 'INSTAGRAM_ENABLE_ACTION_PROCESSING') return String(dryRunConfig.INSTAGRAM_ENABLE_ACTION_PROCESSING);
95+
return dryRunConfig[key as keyof typeof dryRunConfig];
96+
});
97+
98+
const result = await InstagramClientInterface.start(mockRuntime);
99+
100+
expect(result).toBeDefined();
101+
expect(elizaLogger.log).toHaveBeenCalledWith('Instagram client running in dry-run mode');
102+
expect(mockPostService.start).not.toHaveBeenCalled();
103+
expect(mockInteractionService.start).not.toHaveBeenCalled();
104+
});
105+
106+
it('handles errors during startup', async () => {
107+
const error = new Error('Startup failed');
108+
vi.mocked(mockRuntime.getSetting).mockImplementation(() => {
109+
throw error;
110+
});
111+
112+
await expect(InstagramClientInterface.start(mockRuntime)).rejects.toThrow('Startup failed');
113+
expect(elizaLogger.error).toHaveBeenCalledWith('Failed to start Instagram client:', error);
114+
});
115+
116+
it('stops gracefully', async () => {
117+
await InstagramClientInterface.stop(mockRuntime);
118+
expect(elizaLogger.log).toHaveBeenCalledWith('Stopping Instagram client services...');
119+
});
120+
});

packages/client-instagram/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@
2727
},
2828
"devDependencies": {
2929
"tsup": "8.3.5",
30-
"@types/sharp": "^0.32.0"
30+
"@types/sharp": "^0.32.0",
31+
"vitest": "^1.2.1"
3132
},
3233
"scripts": {
3334
"build": "tsup --format esm --dts",
3435
"dev": "tsup --format esm --dts --watch",
35-
"lint": "eslint --fix --cache ."
36+
"lint": "eslint --fix --cache .",
37+
"test": "vitest run",
38+
"test:watch": "vitest"
3639
}
3740
}

0 commit comments

Comments
 (0)