Skip to main content
Glama

documcp

by tosin2013
user-preferences.test.ts16.1 kB
/** * Tests for User Preference Management */ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { promises as fs } from "fs"; import { join } from "path"; import { tmpdir } from "os"; import { UserPreferenceManager, getUserPreferenceManager, clearPreferenceManagerCache, } from "../../src/memory/user-preferences.js"; import { getKnowledgeGraph, initializeKnowledgeGraph, } from "../../src/memory/kg-integration.js"; describe("UserPreferenceManager", () => { let testDir: string; beforeEach(async () => { // Create temporary test directory testDir = join(tmpdir(), `user-prefs-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); // Initialize KG with test directory await initializeKnowledgeGraph(testDir); clearPreferenceManagerCache(); }); afterEach(async () => { clearPreferenceManagerCache(); // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe("Initialization", () => { it("should create default preferences for new user", async () => { const manager = new UserPreferenceManager("test-user"); await manager.initialize(); const prefs = await manager.getPreferences(); expect(prefs.userId).toBe("test-user"); expect(prefs.preferredSSGs).toEqual([]); expect(prefs.documentationStyle).toBe("comprehensive"); expect(prefs.expertiseLevel).toBe("intermediate"); expect(prefs.autoApplyPreferences).toBe(true); }); it("should load existing preferences from knowledge graph", async () => { // Create a user with preferences const kg = await getKnowledgeGraph(); kg.addNode({ id: "user:existing-user", type: "user", label: "existing-user", properties: { userId: "existing-user", preferredSSGs: ["jekyll", "hugo"], documentationStyle: "minimal", expertiseLevel: "advanced", preferredTechnologies: ["typescript"], preferredDiataxisCategories: ["tutorials"], autoApplyPreferences: false, lastActive: "2025-01-01T00:00:00.000Z", }, weight: 1.0, }); const manager = new UserPreferenceManager("existing-user"); await manager.initialize(); const prefs = await manager.getPreferences(); expect(prefs.userId).toBe("existing-user"); expect(prefs.preferredSSGs).toEqual(["jekyll", "hugo"]); expect(prefs.documentationStyle).toBe("minimal"); expect(prefs.expertiseLevel).toBe("advanced"); expect(prefs.autoApplyPreferences).toBe(false); }); it("should handle getPreferences before initialization", async () => { const manager = new UserPreferenceManager("auto-init"); const prefs = await manager.getPreferences(); expect(prefs.userId).toBe("auto-init"); expect(prefs.preferredSSGs).toEqual([]); }); }); describe("Update Preferences", () => { it("should update preferences and save to knowledge graph", async () => { const manager = new UserPreferenceManager("update-test"); await manager.initialize(); await manager.updatePreferences({ documentationStyle: "tutorial-heavy", expertiseLevel: "beginner", preferredTechnologies: ["python", "go"], }); const prefs = await manager.getPreferences(); expect(prefs.documentationStyle).toBe("tutorial-heavy"); expect(prefs.expertiseLevel).toBe("beginner"); expect(prefs.preferredTechnologies).toEqual(["python", "go"]); }); it("should initialize before update if not already initialized", async () => { const manager = new UserPreferenceManager("lazy-init"); await manager.updatePreferences({ expertiseLevel: "advanced", }); const prefs = await manager.getPreferences(); expect(prefs.expertiseLevel).toBe("advanced"); }); }); describe("Track SSG Usage", () => { it("should track successful SSG usage and create preference", async () => { const manager = new UserPreferenceManager("ssg-tracker"); await manager.initialize(); await manager.trackSSGUsage({ ssg: "jekyll", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); const prefs = await manager.getPreferences(); expect(prefs.preferredSSGs).toContain("jekyll"); }); it("should track failed SSG usage", async () => { const manager = new UserPreferenceManager("fail-tracker"); await manager.initialize(); await manager.trackSSGUsage({ ssg: "hugo", success: false, timestamp: "2025-01-01T00:00:00.000Z", }); const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ type: "user_prefers_ssg", }); expect(edges.length).toBeGreaterThan(0); const edge = edges.find((e) => e.target.includes("hugo")); expect(edge).toBeDefined(); expect(edge!.weight).toBe(0.5); // Failed usage has lower weight }); it("should update existing SSG preference", async () => { const manager = new UserPreferenceManager("update-tracker"); await manager.initialize(); // First usage - success await manager.trackSSGUsage({ ssg: "docusaurus", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); // Second usage - success await manager.trackSSGUsage({ ssg: "docusaurus", success: true, timestamp: "2025-01-02T00:00:00.000Z", }); const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ type: "user_prefers_ssg", }); const docEdge = edges.find((e) => e.target.includes("docusaurus")); expect(docEdge!.properties.usageCount).toBe(2); expect(docEdge!.properties.successRate).toBe(1.0); }); it("should calculate average success rate correctly", async () => { const manager = new UserPreferenceManager("avg-tracker"); await manager.initialize(); // Success await manager.trackSSGUsage({ ssg: "mkdocs", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); // Failure await manager.trackSSGUsage({ ssg: "mkdocs", success: false, timestamp: "2025-01-02T00:00:00.000Z", }); const kg = await getKnowledgeGraph(); const edges = await kg.findEdges({ type: "user_prefers_ssg", }); const mkdocsEdge = edges.find((e) => e.target.includes("mkdocs")); expect(mkdocsEdge!.properties.successRate).toBe(0.5); }); it("should create user node if it doesn't exist during tracking", async () => { const manager = new UserPreferenceManager("new-tracker"); // Don't initialize - let trackSSGUsage create it await manager.trackSSGUsage({ ssg: "eleventy", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); const kg = await getKnowledgeGraph(); const userNode = await kg.findNode({ type: "user", properties: { userId: "new-tracker" }, }); expect(userNode).toBeDefined(); }); }); describe("SSG Recommendations", () => { it("should return recommendations sorted by score", async () => { const manager = new UserPreferenceManager("rec-test"); await manager.initialize(); // Track multiple SSGs with different success rates await manager.trackSSGUsage({ ssg: "jekyll", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); await manager.trackSSGUsage({ ssg: "jekyll", success: true, timestamp: "2025-01-02T00:00:00.000Z", }); await manager.trackSSGUsage({ ssg: "hugo", success: true, timestamp: "2025-01-03T00:00:00.000Z", }); const recommendations = await manager.getSSGRecommendations(); expect(recommendations.length).toBeGreaterThan(0); expect(recommendations[0].ssg).toBe("jekyll"); // Higher usage count expect(recommendations[0].score).toBeGreaterThan( recommendations[1].score, ); }); it("should include reason with high success rate", async () => { const manager = new UserPreferenceManager("reason-test"); await manager.initialize(); await manager.trackSSGUsage({ ssg: "docusaurus", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); const recommendations = await manager.getSSGRecommendations(); const docRec = recommendations.find((r) => r.ssg === "docusaurus"); expect(docRec!.reason).toContain("100% success rate"); }); it("should include reason with low success rate", async () => { const manager = new UserPreferenceManager("low-success-test"); await manager.initialize(); // Track both success and failure to get a low rate (not exactly 0) await manager.trackSSGUsage({ ssg: "eleventy", success: true, timestamp: "2025-01-01T00:00:00.000Z", }); await manager.trackSSGUsage({ ssg: "eleventy", success: false, timestamp: "2025-01-02T00:00:00.000Z", }); await manager.trackSSGUsage({ ssg: "eleventy", success: false, timestamp: "2025-01-03T00:00:00.000Z", }); const recommendations = await manager.getSSGRecommendations(); const eleventyRec = recommendations.find((r) => r.ssg === "eleventy"); expect(eleventyRec!.reason).toContain("only"); expect(eleventyRec!.reason).toContain("success rate"); }); it("should return empty array if no user node exists", async () => { const manager = new UserPreferenceManager("no-user"); // Don't initialize or create user node const recommendations = await manager.getSSGRecommendations(); expect(recommendations).toEqual([]); }); }); describe("Apply Preferences to Recommendation", () => { it("should return original recommendation if autoApply is false", async () => { const manager = new UserPreferenceManager("no-auto"); await manager.updatePreferences({ autoApplyPreferences: false, preferredSSGs: ["jekyll"], }); const result = manager.applyPreferencesToRecommendation("hugo", [ "jekyll", "hugo", ]); expect(result.recommended).toBe("hugo"); expect(result.adjustmentReason).toBeUndefined(); }); it("should keep recommendation if it matches preferred SSG", async () => { const manager = new UserPreferenceManager("match-pref"); await manager.updatePreferences({ preferredSSGs: ["jekyll", "hugo"], }); const result = manager.applyPreferencesToRecommendation("jekyll", [ "jekyll", "hugo", "mkdocs", ]); expect(result.recommended).toBe("jekyll"); expect(result.adjustmentReason).toContain("Matches your preferred SSG"); }); it("should switch to preferred SSG if in alternatives", async () => { const manager = new UserPreferenceManager("switch-pref"); await manager.updatePreferences({ preferredSSGs: ["docusaurus"], }); const result = manager.applyPreferencesToRecommendation("jekyll", [ "jekyll", "docusaurus", "hugo", ]); expect(result.recommended).toBe("docusaurus"); expect(result.adjustmentReason).toContain( "Switched to docusaurus based on your usage history", ); }); it("should return original if no preferred SSGs match", async () => { const manager = new UserPreferenceManager("no-match"); await manager.updatePreferences({ preferredSSGs: ["eleventy"], }); const result = manager.applyPreferencesToRecommendation("jekyll", [ "jekyll", "hugo", ]); expect(result.recommended).toBe("jekyll"); expect(result.adjustmentReason).toBeUndefined(); }); it("should return original if no preferences set", async () => { const manager = new UserPreferenceManager("empty-pref"); await manager.initialize(); const result = manager.applyPreferencesToRecommendation("jekyll", [ "jekyll", "hugo", ]); expect(result.recommended).toBe("jekyll"); expect(result.adjustmentReason).toBeUndefined(); }); }); describe("Reset Preferences", () => { it("should reset preferences to defaults", async () => { const manager = new UserPreferenceManager("reset-test"); await manager.updatePreferences({ documentationStyle: "minimal", expertiseLevel: "advanced", preferredSSGs: ["jekyll", "hugo"], }); await manager.resetPreferences(); const prefs = await manager.getPreferences(); expect(prefs.documentationStyle).toBe("comprehensive"); expect(prefs.expertiseLevel).toBe("intermediate"); expect(prefs.preferredSSGs).toEqual([]); }); }); describe("Export/Import Preferences", () => { it("should export preferences as JSON", async () => { const manager = new UserPreferenceManager("export-test"); await manager.updatePreferences({ expertiseLevel: "advanced", preferredSSGs: ["jekyll"], }); const exported = await manager.exportPreferences(); const parsed = JSON.parse(exported); expect(parsed.userId).toBe("export-test"); expect(parsed.expertiseLevel).toBe("advanced"); expect(parsed.preferredSSGs).toEqual(["jekyll"]); }); it("should import preferences from JSON", async () => { const manager = new UserPreferenceManager("import-test"); await manager.initialize(); const importData = { userId: "import-test", preferredSSGs: ["hugo", "docusaurus"], documentationStyle: "tutorial-heavy" as const, expertiseLevel: "beginner" as const, preferredTechnologies: ["python"], preferredDiataxisCategories: ["tutorials" as const], autoApplyPreferences: false, lastUpdated: "2025-01-01T00:00:00.000Z", }; await manager.importPreferences(JSON.stringify(importData)); const prefs = await manager.getPreferences(); expect(prefs.expertiseLevel).toBe("beginner"); expect(prefs.preferredSSGs).toEqual(["hugo", "docusaurus"]); expect(prefs.autoApplyPreferences).toBe(false); }); it("should throw error on userId mismatch during import", async () => { const manager = new UserPreferenceManager("user1"); await manager.initialize(); const importData = { userId: "user2", // Different user ID preferredSSGs: [], documentationStyle: "comprehensive" as const, expertiseLevel: "intermediate" as const, preferredTechnologies: [], preferredDiataxisCategories: [], autoApplyPreferences: true, lastUpdated: "2025-01-01T00:00:00.000Z", }; await expect( manager.importPreferences(JSON.stringify(importData)), ).rejects.toThrow("User ID mismatch"); }); }); describe("Manager Cache", () => { it("should cache preference managers", async () => { const manager1 = await getUserPreferenceManager("cached-user"); const manager2 = await getUserPreferenceManager("cached-user"); expect(manager1).toBe(manager2); // Same instance }); it("should create different managers for different users", async () => { const manager1 = await getUserPreferenceManager("user1"); const manager2 = await getUserPreferenceManager("user2"); expect(manager1).not.toBe(manager2); }); it("should clear cache", async () => { const manager1 = await getUserPreferenceManager("clear-test"); clearPreferenceManagerCache(); const manager2 = await getUserPreferenceManager("clear-test"); expect(manager1).not.toBe(manager2); // Different instances after clear }); }); });

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