#!/usr/bin/env tsx
/**
* End-to-end test for the Gemini Docs MCP server tools.
* This simulates how an MCP client would use the tools.
*/
import { fetchHtml, parseDocPage, formatAsMarkdown, formatAsJson } from "./utils/parser.js";
import { searchDocs, formatSearchResultsAsMarkdown, formatSearchResultsAsJson } from "./utils/search.js";
import { DOCS_BASE_URL, CHARACTER_LIMIT } from "./constants.js";
// Simulate the search tool
async function testSearchTool(query: string, maxResults: number = 10, format: "markdown" | "json" = "markdown"): Promise<string> {
console.log(`\n[search_gemini_docs] query="${query}" max_results=${maxResults} format=${format}`);
const results = searchDocs(query, maxResults);
if (format === "markdown") {
return formatSearchResultsAsMarkdown(results, query);
} else {
return formatSearchResultsAsJson(results, query);
}
}
// Simulate the fetch tool
async function testFetchTool(path: string, format: "markdown" | "json" = "markdown"): Promise<string> {
console.log(`\n[fetch_gemini_doc] path="${path}" format=${format}`);
const cleanPath = path.replace(/^\/+|\/+$/g, "").replace(/^gemini-api\/docs\/?/, "");
const url = cleanPath ? `${DOCS_BASE_URL}/${cleanPath}` : DOCS_BASE_URL;
const html = await fetchHtml(url);
const doc = parseDocPage(html, url);
let content: string;
if (format === "markdown") {
content = formatAsMarkdown(doc);
} else {
content = formatAsJson(doc);
}
if (content.length > CHARACTER_LIMIT) {
content = content.substring(0, CHARACTER_LIMIT) +
"\n\n[Content truncated due to size limit]";
}
return content;
}
async function runE2ETests(): Promise<void> {
console.log("=" .repeat(70));
console.log("Gemini Docs MCP Server - End-to-End Tool Tests");
console.log("=" .repeat(70));
let passed = 0;
let failed = 0;
// Test 1: Search for function calling
try {
const result = await testSearchTool("function calling", 5, "json");
const data = JSON.parse(result);
if (data.results.length > 0 && data.results[0].path === "function-calling") {
console.log("✓ Test 1: Search 'function calling' - PASSED");
passed++;
} else {
console.log("✗ Test 1: Search 'function calling' - FAILED (wrong results)");
failed++;
}
} catch (e) {
console.log(`✗ Test 1: Search 'function calling' - FAILED (${e})`);
failed++;
}
// Test 2: Search for embeddings
try {
const result = await testSearchTool("embeddings", 5, "markdown");
if (result.includes("Embeddings") && result.includes("embeddings")) {
console.log("✓ Test 2: Search 'embeddings' (markdown) - PASSED");
passed++;
} else {
console.log("✗ Test 2: Search 'embeddings' (markdown) - FAILED");
failed++;
}
} catch (e) {
console.log(`✗ Test 2: Search 'embeddings' (markdown) - FAILED (${e})`);
failed++;
}
// Test 3: Fetch embeddings page
try {
const result = await testFetchTool("embeddings", "json");
const data = JSON.parse(result);
if (data.title === "Embeddings" && data.sections.length > 0) {
console.log("✓ Test 3: Fetch 'embeddings' page - PASSED");
passed++;
} else {
console.log("✗ Test 3: Fetch 'embeddings' page - FAILED (wrong content)");
failed++;
}
} catch (e) {
console.log(`✗ Test 3: Fetch 'embeddings' page - FAILED (${e})`);
failed++;
}
// Test 4: Fetch function-calling page
try {
const result = await testFetchTool("function-calling", "markdown");
if (result.includes("Function calling") && result.includes("Gemini API")) {
console.log("✓ Test 4: Fetch 'function-calling' page (markdown) - PASSED");
passed++;
} else {
console.log("✗ Test 4: Fetch 'function-calling' page - FAILED");
failed++;
}
} catch (e) {
console.log(`✗ Test 4: Fetch 'function-calling' page - FAILED (${e})`);
failed++;
}
// Test 5: Fetch main overview page (empty path)
try {
const result = await testFetchTool("", "json");
const data = JSON.parse(result);
if (data.title === "Gemini API" && data.url === DOCS_BASE_URL) {
console.log("✓ Test 5: Fetch main overview page - PASSED");
passed++;
} else {
console.log("✗ Test 5: Fetch main overview page - FAILED");
failed++;
}
} catch (e) {
console.log(`✗ Test 5: Fetch main overview page - FAILED (${e})`);
failed++;
}
// Test 6: Search with no results
try {
const result = await testSearchTool("xyznonexistentquery123", 5, "json");
const data = JSON.parse(result);
if (data.results.length === 0) {
console.log("✓ Test 6: Search with no results - PASSED");
passed++;
} else {
console.log("✗ Test 6: Search with no results - FAILED (got results)");
failed++;
}
} catch (e) {
console.log(`✗ Test 6: Search with no results - FAILED (${e})`);
failed++;
}
// Test 7: Search for vision/image
try {
const result = await testSearchTool("vision", 5, "json");
const data = JSON.parse(result);
if (data.results.some((r: any) => r.path === "image-understanding")) {
console.log("✓ Test 7: Search 'vision' finds image-understanding - PASSED");
passed++;
} else {
console.log("✗ Test 7: Search 'vision' - FAILED (didn't find image-understanding)");
failed++;
}
} catch (e) {
console.log(`✗ Test 7: Search 'vision' - FAILED (${e})`);
failed++;
}
// Test 8: Fetch quickstart page
try {
const result = await testFetchTool("quickstart", "markdown");
if (result.includes("quickstart") && result.includes("SDK")) {
console.log("✓ Test 8: Fetch 'quickstart' page - PASSED");
passed++;
} else {
console.log("✗ Test 8: Fetch 'quickstart' page - FAILED");
failed++;
}
} catch (e) {
console.log(`✗ Test 8: Fetch 'quickstart' page - FAILED (${e})`);
failed++;
}
console.log("\n" + "=" .repeat(70));
console.log(`Results: ${passed} passed, ${failed} failed`);
console.log("=" .repeat(70));
if (failed > 0) {
process.exit(1);
}
}
runE2ETests();