Skip to main content
Glama
ListBoilerplatesTool.pagination.test.ts7.71 kB
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { ListBoilerplatesTool } from '../../src/tools/ListBoilerplatesTool'; import { PaginationHelper } from '../../src/utils/pagination'; // Mock the service vi.mock('../../src/services/BoilerplateService'); describe('ListBoilerplatesTool - Pagination', () => { let tool: ListBoilerplatesTool; const templatesPath = '/test/templates'; beforeEach(() => { vi.clearAllMocks(); tool = new ListBoilerplatesTool(templatesPath, false); }); it('should include cursor parameter in schema', () => { const definition = tool.getDefinition(); expect(definition.inputSchema.properties).toHaveProperty('cursor'); expect(definition.inputSchema.properties.cursor).toEqual({ type: 'string', description: 'Optional pagination cursor to fetch the next page of results. Omit to fetch the first page.', }); }); it('should return paginated results on first page', async () => { // Create mock data with 15 items to test pagination const mockBoilerplates = Array.from({ length: 15 }, (_, i) => ({ name: `scaffold-app-${i + 1}`, description: `Application template ${i + 1}`, instruction: 'Follow the instructions', variables_schema: { type: 'object', properties: { appName: { type: 'string', description: 'App name' }, }, required: ['appName'], }, template_path: `template-${i + 1}`, target_folder: 'apps', includes: ['**/*'], })); // Mock the paginated response const paginatedResult = PaginationHelper.paginate(mockBoilerplates); const spy = vi.spyOn(tool['boilerplateService'], 'listBoilerplates'); spy.mockResolvedValue({ boilerplates: paginatedResult.items, nextCursor: paginatedResult.nextCursor, }); const result = await tool.execute({}); expect(result.content).toBeDefined(); expect(result.content.length).toBeGreaterThan(0); expect(result.content[0].type).toBe('text'); const response = JSON.parse(result.content[0].text); expect(response).toHaveProperty('boilerplates'); expect(Array.isArray(response.boilerplates)).toBe(true); expect(response.boilerplates).toHaveLength(10); expect(response).toHaveProperty('nextCursor'); expect(typeof response.nextCursor).toBe('string'); }); it('should return next page when cursor is provided', async () => { // Create mock data with 15 items const mockBoilerplates = Array.from({ length: 15 }, (_, i) => ({ name: `scaffold-app-${i + 1}`, description: `Application template ${i + 1}`, instruction: 'Follow the instructions', variables_schema: { type: 'object', properties: { appName: { type: 'string', description: 'App name' }, }, required: ['appName'], }, template_path: `template-${i + 1}`, target_folder: 'apps', includes: ['**/*'], })); const spy = vi.spyOn(tool['boilerplateService'], 'listBoilerplates'); // Mock first page const firstPageResult = PaginationHelper.paginate(mockBoilerplates); spy.mockResolvedValueOnce({ boilerplates: firstPageResult.items, nextCursor: firstPageResult.nextCursor, }); const firstPageResponse = await tool.execute({}); const firstPage = JSON.parse(firstPageResponse.content[0].text); expect(firstPage.boilerplates).toHaveLength(10); expect(firstPage.nextCursor).toBeDefined(); // Mock second page const secondPageResult = PaginationHelper.paginate(mockBoilerplates, firstPage.nextCursor); spy.mockResolvedValueOnce({ boilerplates: secondPageResult.items, nextCursor: secondPageResult.nextCursor, }); const secondPageResponse = await tool.execute({ cursor: firstPage.nextCursor }); const secondPage = JSON.parse(secondPageResponse.content[0].text); expect(secondPage).toHaveProperty('boilerplates'); expect(Array.isArray(secondPage.boilerplates)).toBe(true); expect(secondPage.boilerplates).toHaveLength(5); expect(firstPage.boilerplates[0].name).not.toBe(secondPage.boilerplates[0].name); }); it('should pass cursor parameter to service', async () => { const cursor = PaginationHelper.encodeCursor(1); const spy = vi.spyOn(tool['boilerplateService'], 'listBoilerplates'); spy.mockResolvedValue({ boilerplates: [], nextCursor: undefined, }); await tool.execute({ cursor }); expect(spy).toHaveBeenCalledWith(cursor); }); it('should respect page size of 10 items', async () => { // Create mock data with 25 items const mockBoilerplates = Array.from({ length: 25 }, (_, i) => ({ name: `scaffold-app-${i + 1}`, description: `Application template ${i + 1}`, instruction: 'Follow the instructions', variables_schema: { type: 'object', properties: { appName: { type: 'string', description: 'App name' }, }, required: ['appName'], }, template_path: `template-${i + 1}`, target_folder: 'apps', includes: ['**/*'], })); const paginatedResult = PaginationHelper.paginate(mockBoilerplates); const spy = vi.spyOn(tool['boilerplateService'], 'listBoilerplates'); spy.mockResolvedValue({ boilerplates: paginatedResult.items, nextCursor: paginatedResult.nextCursor, }); const result = await tool.execute({}); const response = JSON.parse(result.content[0].text); // Should have exactly 10 items on first page expect(response.boilerplates.length).toBe(10); }); it('should correctly encode/decode cursor between pages', async () => { const mockBoilerplates = Array.from({ length: 25 }, (_, i) => ({ name: `scaffold-app-${i + 1}`, description: `Application template ${i + 1}`, instruction: 'Follow the instructions', variables_schema: { type: 'object', properties: { appName: { type: 'string', description: 'App name' }, }, required: ['appName'], }, template_path: `template-${i + 1}`, target_folder: 'apps', includes: ['**/*'], })); const spy = vi.spyOn(tool['boilerplateService'], 'listBoilerplates'); // First page const firstPageResult = PaginationHelper.paginate(mockBoilerplates); spy.mockResolvedValueOnce({ boilerplates: firstPageResult.items, nextCursor: firstPageResult.nextCursor, _meta: firstPageResult._meta, }); const firstPageResponse = await tool.execute({}); const firstPage = JSON.parse(firstPageResponse.content[0].text); expect(firstPage.nextCursor).toBe('10'); // Points to index 10 expect(firstPage._meta).toEqual({ total: 25, offset: 0, limit: 10 }); // Decode cursor to verify it points to index 10 const decoded = PaginationHelper.decodeCursor(firstPage.nextCursor!); expect(decoded).toBe(10); // Second page const secondPageResult = PaginationHelper.paginate(mockBoilerplates, firstPage.nextCursor); spy.mockResolvedValueOnce({ boilerplates: secondPageResult.items, nextCursor: secondPageResult.nextCursor, _meta: secondPageResult._meta, }); const secondPageResponse = await tool.execute({ cursor: firstPage.nextCursor }); const secondPage = JSON.parse(secondPageResponse.content[0].text); expect(secondPage.nextCursor).toBe('20'); // Points to index 20 expect(secondPage._meta).toEqual({ total: 25, offset: 10, limit: 10 }); // Decode cursor to verify it points to index 20 const decoded2 = PaginationHelper.decodeCursor(secondPage.nextCursor!); expect(decoded2).toBe(20); }); });

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/AgiFlow/aicode-toolkit'

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