ActivityWatch MCP Server

import { describe, expect, test, jest, beforeEach } from '@jest/globals'; import axios from 'axios'; import { activitywatch_get_settings_tool, SettingsResponse } from './getSettings.js'; // Mock axios jest.mock('axios'); const mockedAxios = axios as jest.Mocked<typeof axios>; describe('activitywatch_get_settings_tool', () => { beforeEach(() => { jest.clearAllMocks(); // Set the NODE_ENV to test to avoid extra guidance text process.env.NODE_ENV = 'test'; }); test('returns all settings when no key provided', async () => { // Mock the response const mockSettings: SettingsResponse = { setting1: 'value1', setting2: 'value2', nested: { key: 'value' } }; mockedAxios.get.mockResolvedValueOnce({ data: mockSettings }); // Call the handler const result = await activitywatch_get_settings_tool.handler({}); // Expectations expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5600/api/0/settings'); expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockSettings, null, 2) } ] }); }); test('returns specific setting when key is provided', async () => { // Mock the response const mockSetting = 'specific-value'; mockedAxios.get.mockResolvedValueOnce({ data: mockSetting }); // Call the handler const result = await activitywatch_get_settings_tool.handler({ key: 'setting1' }); // Expectations expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5600/api/0/settings/setting1'); expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockSetting, null, 2) } ] }); }); test('properly encodes URI components in key parameter', async () => { // Mock the response const mockSetting = { value: 'test' }; mockedAxios.get.mockResolvedValueOnce({ data: mockSetting }); // Call the handler with a key that needs encoding const result = await activitywatch_get_settings_tool.handler({ key: 'complex/key with spaces' }); // Expectations - check that the URL was properly encoded expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5600/api/0/settings/complex%2Fkey%20with%20spaces'); expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockSetting, null, 2) } ] }); }); test('handles API error correctly', async () => { // Create an error object that more closely resembles a real Axios error const error = new Error('Request failed with status code 404') as any; error.isAxiosError = true; error.message = 'Request failed with status code 404'; error.response = { status: 404, data: { error: 'Setting not found' } }; // Mock the error mockedAxios.get.mockRejectedValueOnce(error); // Make sure isAxiosError returns true for this error mockedAxios.isAxiosError.mockImplementation((val): val is any => true); // Call the handler const result = await activitywatch_get_settings_tool.handler({ key: 'nonexistent' }); // Expectations expect(result).toHaveProperty('isError', true); expect(result.content[0].text).toContain('Failed to fetch settings'); expect(result.content[0].text).toContain('Status code: 404'); expect(result.content[0].text).toContain('Setting not found'); }); test('handles network error correctly', async () => { // Create an error object that more closely resembles a real Axios error const error = new Error('Network Error') as any; error.isAxiosError = true; error.message = 'Network Error'; // Mock the error mockedAxios.get.mockRejectedValueOnce(error); // Make sure isAxiosError returns true mockedAxios.isAxiosError.mockImplementation((val): val is any => true); // Call the handler const result = await activitywatch_get_settings_tool.handler({}); // Expectations expect(result).toHaveProperty('isError', true); expect(result.content[0].text).toContain('Failed to fetch settings: Network Error'); expect(result.content[0].text).toContain('This appears to be a network or connection error'); }); test('handles validation with undefined key parameter', async () => { // Mock the response const mockSettings: SettingsResponse = { setting1: 'value1', setting2: 'value2' }; mockedAxios.get.mockResolvedValueOnce({ data: mockSettings }); // Call the handler with undefined key const result = await activitywatch_get_settings_tool.handler({ key: undefined }); // Expectations - should call the endpoint for all settings expect(mockedAxios.get).toHaveBeenCalledWith('http://localhost:5600/api/0/settings'); expect(result).toEqual({ content: [ { type: 'text', text: JSON.stringify(mockSettings, null, 2) } ] }); }); test('adds helpful guidance when not in test mode', async () => { // Temporarily set NODE_ENV to something other than test const originalNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = 'development'; // Mock the response const mockSettings: SettingsResponse = { setting1: 'value1', setting2: 'value2' }; mockedAxios.get.mockResolvedValueOnce({ data: mockSettings }); // Call the handler const result = await activitywatch_get_settings_tool.handler({}); // Expectations - verify that guidance text is included expect(result.content[0].text).toContain(JSON.stringify(mockSettings, null, 2)); expect(result.content[0].text).toContain('Showing all ActivityWatch settings'); expect(result.content[0].text).toContain('To get a specific setting'); // Restore the original NODE_ENV process.env.NODE_ENV = originalNodeEnv; }); });