Skip to main content
Glama
limitless-client.test.ts8.29 kB
import { describe, it, expect, vi, beforeEach } from "vitest"; import axios from "axios"; import { LimitlessClient } from "../src/limitless/client.js"; import type { LimitlessConfig, ListLifelogsResponse, LifelogEntry } from "../src/limitless/types.js"; // Mock axios vi.mock("axios"); const mockedAxios = vi.mocked(axios); describe("LimitlessClient", () => { let client: LimitlessClient; let mockAxiosInstance: any; const config: LimitlessConfig = { apiKey: "test-api-key", baseUrl: "https://api.limitless.ai", }; beforeEach(() => { vi.clearAllMocks(); // Create mock axios instance mockAxiosInstance = { get: vi.fn(), post: vi.fn(), put: vi.fn(), delete: vi.fn(), }; mockedAxios.create.mockReturnValue(mockAxiosInstance); client = new LimitlessClient(config); }); describe("constructor", () => { it("should create axios instance with correct config", () => { expect(mockedAxios.create).toHaveBeenCalledWith({ baseURL: config.baseUrl, headers: { "X-API-Key": config.apiKey, "Content-Type": "application/json", }, timeout: 30000, }); }); }); describe("getLifelogs", () => { it("should fetch lifelog entries successfully", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [ { id: "entry_1", title: "Test conversation", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "Test content", type: "text", }, ], }, ], }, meta: { lifelogs: { count: 1, }, }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.getLifelogs({ date: "2024-01-15" }); expect(mockAxiosInstance.get).toHaveBeenCalledWith("/v1/lifelogs", { params: { date: "2024-01-15" }, }); expect(result).toEqual(mockResponse); }); it("should use default parameters when none provided", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [], }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); await client.getLifelogs(); expect(mockAxiosInstance.get).toHaveBeenCalledWith("/v1/lifelogs", { params: { limit: 10 }, }); }); it("should handle API errors gracefully", async () => { const errorResponse = { isAxiosError: true, response: { status: 401, data: { message: "Invalid API key" }, }, message: "Request failed with status code 401", }; // Mock axios.isAxiosError to return true for our error vi.mocked(axios.isAxiosError).mockReturnValue(true); mockAxiosInstance.get.mockRejectedValue(errorResponse); await expect(client.getLifelogs()).rejects.toThrow( "Limitless API error (401): Invalid API key" ); }); it("should handle network errors", async () => { const networkError = new Error("Network error"); // Ensure axios.isAxiosError returns false for regular Error vi.mocked(axios.isAxiosError).mockReturnValue(false); mockAxiosInstance.get.mockRejectedValue(networkError); await expect(client.getLifelogs()).rejects.toThrow( "Limitless API error: Network error" ); }); }); describe("getLifelog", () => { it("should fetch specific lifelog entry successfully", async () => { const mockEntry: LifelogEntry = { id: "entry_123", title: "Detailed conversation", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "Detailed content", type: "text", }, ], }; const mockResponse = { data: { lifelog: mockEntry, }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.getLifelog("entry_123"); expect(mockAxiosInstance.get).toHaveBeenCalledWith("/v1/lifelogs/entry_123"); expect(result).toEqual(mockEntry); }); it("should handle 404 errors for non-existent entries", async () => { const errorResponse = { isAxiosError: true, response: { status: 404, data: { message: "Entry not found" }, }, message: "Request failed with status code 404", }; // Mock axios.isAxiosError to return true for our error vi.mocked(axios.isAxiosError).mockReturnValue(true); mockAxiosInstance.get.mockRejectedValue(errorResponse); await expect(client.getLifelog("non_existent")).rejects.toThrow( "Limitless API error (404): Entry not found" ); }); }); describe("searchLifelogs", () => { it("should search lifelog entries and filter results", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [ { id: "entry_1", title: "Planning discussion", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "This is about project planning", type: "text", }, ], }, { id: "entry_2", title: "Other topic", startTime: "2024-01-15T11:00:00Z", endTime: "2024-01-15T12:00:00Z", contents: [ { content: "Unrelated content", type: "text", }, ], }, ], }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.searchLifelogs({ query: "project planning", limit: 5, }); expect(result.data.lifelogs).toHaveLength(1); expect(result.data.lifelogs[0].id).toBe("entry_1"); }); it("should search in both content and title", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [ { id: "entry_1", title: "Meeting summary", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "Some content", type: "text", }, ], }, ], }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.searchLifelogs({ query: "meeting", limit: 5, }); expect(result.data.lifelogs).toHaveLength(1); }); it("should handle case-insensitive search", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [ { id: "entry_1", title: "Discussion", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "PROJECT PLANNING session", type: "text", }, ], }, ], }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.searchLifelogs({ query: "project planning", limit: 5, }); expect(result.data.lifelogs).toHaveLength(1); }); it("should return empty results when no matches found", async () => { const mockResponse: ListLifelogsResponse = { data: { lifelogs: [ { id: "entry_1", title: "Different topic", startTime: "2024-01-15T09:00:00Z", endTime: "2024-01-15T10:00:00Z", contents: [ { content: "Unrelated content", type: "text", }, ], }, ], }, }; mockAxiosInstance.get.mockResolvedValue({ data: mockResponse }); const result = await client.searchLifelogs({ query: "nonexistent topic", limit: 5, }); expect(result.data.lifelogs).toHaveLength(0); }); }); describe("error handling", () => { it("should handle unknown errors", async () => { vi.mocked(axios.isAxiosError).mockReturnValue(false); mockAxiosInstance.get.mockRejectedValue("unknown error"); await expect(client.getLifelogs()).rejects.toThrow( "Limitless API error: unknown error" ); }); it("should handle axios errors without response", async () => { const axiosError = { isAxiosError: true, message: "Request timeout", }; // Mock axios.isAxiosError to return true vi.mocked(axios.isAxiosError).mockReturnValue(true); mockAxiosInstance.get.mockRejectedValue(axiosError); await expect(client.getLifelogs()).rejects.toThrow( "Limitless API error (unknown): Request timeout" ); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hint-Services/mcp-todoist'

If you have feedback or need assistance with the MCP directory API, please join our Discord server