Skip to main content
Glama
searchContent.test.ts10.6 kB
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { Server } from '../../server.js'; import { getSearchContentTool } from './searchContent.js'; export const mockSearchContentResponse = { next: 'next-page-url', prev: 'prev-page-url', pageIndex: 0, startIndex: 0, total: 3, limit: 100, items: [ { uri: 'test-uri-1', content: { type: 'workbook', luid: 'workbook-1-luid', title: 'Sales Dashboard', ownerName: 'John Doe', ownerId: 123, ownerEmail: 'john.doe@example.com', projectId: 123456, projectName: 'Finance', containerName: 'Finance', hitsTotal: 150, hitsSmallSpanTotal: 10, hitsMediumSpanTotal: 25, hitsLargeSpanTotal: 50, favoritesTotal: 5, modifiedTime: '2024-01-15T10:30:00Z', createdTime: '2023-12-01T09:00:00Z', tags: ['dashboard', 'sales'], }, }, { uri: 'test-uri-2', content: { type: 'datasource', luid: 'datasource-1-luid', title: 'Customer Data', ownerName: 'Jane Smith', ownerId: 456, ownerEmail: 'jane.smith@example.com', projectId: 987654, projectName: 'Marketing', containerName: 'Marketing', hitsTotal: 75, hitsSmallSpanTotal: 5, hitsMediumSpanTotal: 15, hitsLargeSpanTotal: 30, favoritesTotal: 3, modifiedTime: '2024-01-10T14:20:00Z', createdTime: '2023-11-15T11:30:00Z', }, }, { uri: 'test-uri-2', content: { type: 'unifieddatasource', luid: 'unifieddatasource-1-luid', datasourceLuid: 'datasource-1-luid', title: 'Customer Data', ownerName: 'Jane Smith', ownerId: 456, ownerEmail: 'jane.smith@example.com', projectId: 987654, projectName: 'Marketing', containerName: 'Marketing', hitsTotal: 75, hitsSmallSpanTotal: 5, hitsMediumSpanTotal: 15, hitsLargeSpanTotal: 30, favoritesTotal: 3, modifiedTime: '2024-01-10T14:20:00Z', createdTime: '2023-11-15T11:30:00Z', }, }, ], }; const mocks = vi.hoisted(() => ({ mockSearchContent: vi.fn(), })); vi.mock('../../restApiInstance.js', () => ({ useRestApi: vi.fn().mockImplementation(async ({ callback }) => callback({ contentExplorationMethods: { searchContent: mocks.mockSearchContent, }, siteId: 'test-site-id', }), ), })); describe('searchContentTool', () => { beforeEach(() => { vi.clearAllMocks(); }); it('should create a tool instance with correct properties', () => { const searchContentTool = getSearchContentTool(new Server()); expect(searchContentTool.name).toBe('search-content'); expect(searchContentTool.description).toContain('searches across all supported content types'); expect(searchContentTool.paramsSchema).toMatchObject({ terms: expect.any(Object), limit: expect.any(Object), orderBy: expect.any(Object), filter: expect.any(Object), }); }); it('should successfully search content with basic parameters', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ terms: 'sales dashboard' }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: 'sales dashboard', page: 0, limit: 100, orderBy: undefined, filter: undefined, }); const responseData = JSON.parse(result.content[0].text as string); expect(responseData).toHaveLength(2); expect(responseData[0]).toMatchObject({ type: 'workbook', title: 'Sales Dashboard', ownerName: 'John Doe', totalViewCount: 150, viewCountLastMonth: 10, containerName: 'Finance', }); }); it('should search content with custom limit', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ terms: 'sales', limit: 100 }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: 'sales', page: 0, limit: 100, orderBy: undefined, filter: undefined, }); }); it('should search content with orderBy parameter', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ terms: 'dashboard', orderBy: [{ method: 'hitsTotal', sortDirection: 'desc' }], }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: 'dashboard', page: 0, limit: 100, orderBy: 'hitsTotal:desc', filter: undefined, }); }); it('should search content with multiple orderBy criteria', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ terms: 'dashboard', orderBy: [ { method: 'hitsTotal', sortDirection: 'desc' }, { method: 'hitsSmallSpanTotal', sortDirection: 'asc' }, ], }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: 'dashboard', page: 0, limit: 100, orderBy: 'hitsTotal:desc,hitsSmallSpanTotal:asc', filter: undefined, }); }); it('should search content with content type filter', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ filter: { contentTypes: ['workbook', 'datasource'] }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: undefined, filter: 'type:in:[workbook,datasource]', }); }); it('should search content with single content type filter', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ filter: { contentTypes: ['workbook'] }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: undefined, filter: 'type:eq:workbook', }); }); it('should search content with owner ID filter', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ filter: { ownerIds: [123, 456] }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: undefined, filter: 'ownerId:in:[123,456]', }); }); it('should search content with modified time range filter', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ filter: { modifiedTime: { startDate: '2024-01-01T00:00:00Z', endDate: '2024-01-31T23:59:59Z', }, }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: undefined, filter: 'modifiedTime:gte:2024-01-01T00:00:00Z,modifiedTime:lte:2024-01-31T23:59:59Z', }); }); it('should search content with complex filter combination', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ terms: 'sales', filter: { contentTypes: ['workbook'], ownerIds: [123], modifiedTime: { startDate: '2024-01-01T00:00:00Z', }, }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: 'sales', page: 0, limit: 100, orderBy: undefined, filter: 'type:eq:workbook,ownerId:eq:123,modifiedTime:gte:2024-01-01T00:00:00Z', }); }); it('should allow downstreamWorkbookCount with table content type', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ orderBy: [{ method: 'downstreamWorkbookCount', sortDirection: 'desc' }], filter: { contentTypes: ['table'] }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: 'downstreamWorkbookCount:desc', filter: 'type:eq:table', }); }); it('should allow downstreamWorkbookCount with database content type', async () => { mocks.mockSearchContent.mockResolvedValue(mockSearchContentResponse); const result = await getToolResult({ orderBy: [{ method: 'downstreamWorkbookCount', sortDirection: 'desc' }], filter: { contentTypes: ['database'] }, }); expect(result.isError).toBe(false); expect(mocks.mockSearchContent).toHaveBeenCalledWith({ terms: undefined, page: 0, limit: 100, orderBy: 'downstreamWorkbookCount:desc', filter: 'type:eq:database', }); }); it('should handle API errors gracefully', async () => { const errorMessage = 'Search API Error'; mocks.mockSearchContent.mockRejectedValue(new Error(errorMessage)); const result = await getToolResult({ terms: 'test' }); expect(result.isError).toBe(true); expect(result.content[0].text).toContain(errorMessage); }); it('should handle empty search results', async () => { const emptyResponse = { ...mockSearchContentResponse, total: 0, items: [], }; mocks.mockSearchContent.mockResolvedValue(emptyResponse); const result = await getToolResult({ terms: 'nonexistent' }); expect(result.isError).toBe(false); const responseData = result.content[0].text as string; expect(responseData).toEqual( 'No search results were found. Either none exist or you do not have permission to view them.', ); }); }); async function getToolResult(params: any): Promise<CallToolResult> { const searchContentTool = getSearchContentTool(new Server()); return await searchContentTool.callback(params, { signal: new AbortController().signal, requestId: 'test-request-id', sendNotification: vi.fn(), sendRequest: vi.fn(), }); }

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/datalabs89/tableau-mcp'

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