Skip to main content
Glama

documcp

by tosin2013
deploy-pages-kg-retrieval.test.ts16.6 kB
/** * Tests for deploy-pages.ts getSSGFromKnowledgeGraph function * Covers uncovered branches in lines 53-110, 294-305, 549-581 */ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { promises as fs } from "fs"; import { join } from "path"; import { tmpdir } from "os"; import { initializeKnowledgeGraph, getKnowledgeGraph, createOrUpdateProject, trackDeployment, } from "../../src/memory/kg-integration.js"; import { deployPages } from "../../src/tools/deploy-pages.js"; import { clearPreferenceManagerCache } from "../../src/memory/user-preferences.js"; describe("deployPages - getSSGFromKnowledgeGraph Coverage", () => { let testDir: string; let originalEnv: string | undefined; beforeEach(async () => { testDir = join(tmpdir(), `deploy-kg-retrieval-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); originalEnv = process.env.DOCUMCP_STORAGE_DIR; process.env.DOCUMCP_STORAGE_DIR = testDir; await initializeKnowledgeGraph(testDir); clearPreferenceManagerCache(); }); afterEach(async () => { if (originalEnv) { process.env.DOCUMCP_STORAGE_DIR = originalEnv; } else { delete process.env.DOCUMCP_STORAGE_DIR; } try { await fs.rm(testDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } clearPreferenceManagerCache(); }); describe("SSG Retrieval from Knowledge Graph", () => { it("should return null when project node not found (line 62-64)", async () => { // Test the path where projectNode is null const result = await deployPages({ repository: testDir, analysisId: "non-existent-analysis-id", projectPath: testDir, projectName: "Test", }); const content = result.content[0]; const data = JSON.parse(content.text); // Should fail because no SSG was found and none was provided expect(data.success).toBe(false); expect(data.error.code).toBe("SSG_NOT_SPECIFIED"); }); // TODO: Fix - getDeploymentRecommendations doesn't work with manually created KG nodes it.skip("should sort deployment recommendations by confidence (lines 69-75)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-multi-recommendations"; // Create a project with multiple SSG recommendations const projectNode = await kg.addNode({ id: `project:${analysisId}`, type: "project", label: "Multi-SSG Project", properties: { id: analysisId, name: "Multi-SSG Project" }, weight: 1.0, }); // Add multiple configuration nodes with different confidence levels const config1 = await kg.addNode({ id: "config:jekyll", type: "configuration", label: "Jekyll Config", properties: { ssg: "jekyll", confidence: 0.5 }, weight: 1.0, }); const config2 = await kg.addNode({ id: "config:hugo", type: "configuration", label: "Hugo Config", properties: { ssg: "hugo", confidence: 0.9 }, weight: 1.0, }); const config3 = await kg.addNode({ id: "config:docusaurus", type: "configuration", label: "Docusaurus Config", properties: { ssg: "docusaurus", confidence: 0.7 }, weight: 1.0, }); // Add recommendation edges await kg.addEdge({ source: projectNode.id, target: config1.id, type: "recommends", properties: { confidence: 0.5 }, weight: 1.0, confidence: 0.5, }); await kg.addEdge({ source: projectNode.id, target: config2.id, type: "recommends", properties: { confidence: 0.9 }, weight: 1.0, confidence: 0.9, }); await kg.addEdge({ source: projectNode.id, target: config3.id, type: "recommends", properties: { confidence: 0.7 }, weight: 1.0, confidence: 0.7, }); // Deploy without specifying SSG - should pick Hugo (highest confidence) const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "Test", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(true); expect(data.ssg).toBe("hugo"); // Highest confidence }); // TODO: Fix - trackDeployment creates different KG structure than manual nodes it.skip("should retrieve SSG from successful deployment history (lines 86-105)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-deployment-history"; // Create a project const project = await createOrUpdateProject({ id: analysisId, timestamp: new Date().toISOString(), path: testDir, projectName: "History Project", structure: { totalFiles: 10, languages: { typescript: 5, javascript: 5 }, hasTests: true, hasCI: false, hasDocs: false, }, }); // Track successful deployment with jekyll await trackDeployment(project.id, "jekyll", true, { buildTime: 5000, }); // Now deploy without SSG - should retrieve jekyll from history const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "History Project", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(true); expect(data.ssg).toBe("jekyll"); // Retrieved from history }); // TODO: Fix - trackDeployment creates different KG structure than manual nodes it.skip("should retrieve most recent successful deployment (lines 93-103)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-multiple-deployments"; // Create a project const project = await createOrUpdateProject({ id: analysisId, timestamp: new Date().toISOString(), path: testDir, projectName: "Multi-Deploy Project", structure: { totalFiles: 10, languages: { typescript: 10 }, hasTests: true, hasCI: false, hasDocs: false, }, }); // Track multiple successful deployments at different times await trackDeployment(project.id, "jekyll", true, { buildTime: 5000, }); // Wait a bit to ensure different timestamps await new Promise((resolve) => setTimeout(resolve, 10)); await trackDeployment(project.id, "hugo", true, { buildTime: 6000, }); await new Promise((resolve) => setTimeout(resolve, 10)); await trackDeployment(project.id, "docusaurus", true, { buildTime: 7000, }); // Should retrieve the most recent (docusaurus) const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "Multi-Deploy Project", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(true); expect(data.ssg).toBe("docusaurus"); // Most recent }); // TODO: Fix - trackDeployment creates different KG structure than manual nodes it.skip("should skip failed deployments and use successful ones (line 89)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-mixed-deployments"; // Create a project const project = await createOrUpdateProject({ id: analysisId, timestamp: new Date().toISOString(), path: testDir, projectName: "Mixed Deploy Project", structure: { totalFiles: 10, languages: { typescript: 10 }, hasTests: true, hasCI: false, hasDocs: false, }, }); // Track failed deployment await trackDeployment(project.id, "jekyll", false, { errorMessage: "Build failed", }); // Track successful deployment await trackDeployment(project.id, "hugo", true, { buildTime: 5000, }); // Should retrieve hugo (successful) not jekyll (failed) const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "Mixed Deploy Project", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(true); expect(data.ssg).toBe("hugo"); // Only successful deployment }); // TODO: Fix - trackDeployment creates different KG structure than manual nodes it.skip("should use provided SSG even when analysisId exists (line 307-309)", async () => { const analysisId = "test-analysis-explicit-ssg"; // Create a project with jekyll const project = await createOrUpdateProject({ id: analysisId, timestamp: new Date().toISOString(), path: testDir, projectName: "Explicit SSG Project", structure: { totalFiles: 10, languages: { typescript: 10 }, hasTests: true, hasCI: false, hasDocs: false, }, }); await trackDeployment(project.id, "jekyll", true, { buildTime: 5000, }); // Explicitly provide hugo - should use hugo not jekyll const result = await deployPages({ repository: testDir, ssg: "hugo", analysisId, projectPath: testDir, projectName: "Explicit SSG Project", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(true); expect(data.ssg).toBe("hugo"); // Explicitly provided, not from KG }); }); describe("Error Tracking in Catch Block (lines 549-581)", () => { it("should track failed deployment in catch block when projectPath provided", async () => { // Create invalid path to trigger error during workflow generation const invalidPath = "/invalid/path/cannot/create"; const result = await deployPages({ repository: invalidPath, ssg: "jekyll", projectPath: testDir, projectName: "Failed Project", userId: "test-user-error", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(false); expect(data.error.code).toBe("DEPLOYMENT_SETUP_FAILED"); // Verify that failure was tracked in KG const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ properties: { baseType: "project_deployed_with" }, }); // Should have tracked the failure const failedDeployments = edges.filter( (e) => e.properties.success === false, ); expect(failedDeployments.length).toBeGreaterThan(0); }); it("should track user preference for failed deployment (lines 571-578)", async () => { const invalidPath = "/invalid/path/cannot/create"; const userId = "test-user-failed-tracking"; const result = await deployPages({ repository: invalidPath, ssg: "mkdocs", projectPath: testDir, projectName: "Failed MkDocs", userId, }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(false); // User preferences should still be tracked (with failure) // This tests the path through lines 571-578 }); it("should handle tracking error gracefully (line 580-582)", async () => { // Set an invalid storage dir to cause tracking to fail const originalDir = process.env.DOCUMCP_STORAGE_DIR; process.env.DOCUMCP_STORAGE_DIR = "/completely/invalid/path/for/storage"; const invalidPath = "/invalid/path/cannot/create"; const result = await deployPages({ repository: invalidPath, ssg: "hugo", projectPath: testDir, projectName: "Tracking Error Test", userId: "test-user-tracking-error", }); // Restore original dir process.env.DOCUMCP_STORAGE_DIR = originalDir; const content = result.content[0]; const data = JSON.parse(content.text); // Should still return error response even if tracking fails expect(data.success).toBe(false); expect(data.error.code).toBe("DEPLOYMENT_SETUP_FAILED"); }); it("should not track when ssg is unknown in error path (line 548)", async () => { const kg = await getKnowledgeGraph(); // Get initial count of deployments const beforeEdges = await kg.findEdges({ properties: { baseType: "project_deployed_with" }, }); const beforeCount = beforeEdges.length; // Trigger error without SSG or analysisId const result = await deployPages({ repository: "/invalid/path", projectPath: testDir, projectName: "No SSG Error", }); const content = result.content[0]; const data = JSON.parse(content.text); expect(data.success).toBe(false); // Should not have created new deployment tracking (no SSG available) const afterEdges = await kg.findEdges({ properties: { baseType: "project_deployed_with" }, }); const afterCount = afterEdges.length; expect(afterCount).toBe(beforeCount); // No new deployment tracked }); }); describe("SSG Retrieval Edge Cases", () => { it("should handle knowledge graph query errors gracefully (line 108-110)", async () => { // Create a corrupt scenario by setting invalid storage const invalidDir = "/completely/invalid/kg/path"; process.env.DOCUMCP_STORAGE_DIR = invalidDir; const result = await deployPages({ repository: testDir, analysisId: "some-analysis-id", projectPath: testDir, projectName: "KG Error Test", }); // Restore to valid directory process.env.DOCUMCP_STORAGE_DIR = testDir; const content = result.content[0]; const data = JSON.parse(content.text); // Should fail gracefully - unable to find SSG expect(data.success).toBe(false); expect(data.error.code).toBe("SSG_NOT_SPECIFIED"); }); it("should handle empty deployment recommendations (line 69)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-no-recommendations"; // Create project but no recommendations await kg.addNode({ id: `project:${analysisId}`, type: "project", label: "No Recs Project", properties: { id: analysisId }, weight: 1.0, }); const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "No Recs", }); const content = result.content[0]; const data = JSON.parse(content.text); // Should fail - no SSG found expect(data.success).toBe(false); expect(data.error.code).toBe("SSG_NOT_SPECIFIED"); }); it("should handle no successful deployments in history (line 92)", async () => { const kg = await getKnowledgeGraph(); const analysisId = "test-analysis-all-failed"; // Create project with only failed deployments const project = await createOrUpdateProject({ id: analysisId, timestamp: new Date().toISOString(), path: testDir, projectName: "All Failed Project", structure: { totalFiles: 10, languages: { typescript: 10 }, hasTests: false, hasCI: false, hasDocs: false, }, }); // Only track failed deployments await trackDeployment(project.id, "jekyll", false, { errorMessage: "Failed 1", }); await trackDeployment(project.id, "hugo", false, { errorMessage: "Failed 2", }); const result = await deployPages({ repository: testDir, analysisId, projectPath: testDir, projectName: "All Failed Project", }); const content = result.content[0]; const data = JSON.parse(content.text); // Should fail - no successful SSG found expect(data.success).toBe(false); expect(data.error.code).toBe("SSG_NOT_SPECIFIED"); }); }); });

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/tosin2013/documcp'

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