Skip to main content
Glama

Task Trellis MCP

checkHierarchicalPrerequisitesComplete.test.ts12.1 kB
import { TrellisObject } from "../../models/TrellisObject"; import { TrellisObjectPriority } from "../../models/TrellisObjectPriority"; import { TrellisObjectStatus } from "../../models/TrellisObjectStatus"; import { TrellisObjectType } from "../../models/TrellisObjectType"; import { Repository } from "../../repositories/Repository"; import { checkHierarchicalPrerequisitesComplete } from "../checkHierarchicalPrerequisitesComplete"; import { checkPrerequisitesComplete } from "../checkPrerequisitesComplete"; // Mock the checkPrerequisitesComplete function jest.mock("../checkPrerequisitesComplete"); const mockCheckPrerequisitesComplete = jest.mocked(checkPrerequisitesComplete); describe("checkHierarchicalPrerequisitesComplete", () => { let mockRepository: jest.Mocked<Repository>; beforeEach(() => { mockRepository = { getObjectById: jest.fn(), getObjects: jest.fn(), saveObject: jest.fn(), deleteObject: jest.fn(), getChildrenOf: jest.fn(), }; jest.clearAllMocks(); }); const createMockObject = ( id: string, parent: string | null = null, prerequisites: string[] = [], ): TrellisObject => ({ id, type: TrellisObjectType.TASK, title: `Test ${id}`, status: TrellisObjectStatus.OPEN, priority: TrellisObjectPriority.MEDIUM, parent, prerequisites, affectedFiles: new Map(), log: [], schema: "v1.0", childrenIds: [], created: "2023-01-01T00:00:00.000Z", updated: "2023-01-01T00:00:00.000Z", body: "", }); describe("object with no prerequisites and no parent", () => { it("should return true when object has no prerequisites and no parent", async () => { const testObject = createMockObject("T-test"); mockCheckPrerequisitesComplete.mockResolvedValue(true); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).not.toHaveBeenCalled(); }); }); describe("object with prerequisites but no parent", () => { it("should return true when object has complete prerequisites and no parent", async () => { const testObject = createMockObject("T-test", undefined, ["T-prereq"]); mockCheckPrerequisitesComplete.mockResolvedValue(true); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).not.toHaveBeenCalled(); }); it("should return false when object has incomplete prerequisites", async () => { const testObject = createMockObject("T-test", undefined, ["T-prereq"]); mockCheckPrerequisitesComplete.mockResolvedValue(false); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(false); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).not.toHaveBeenCalled(); }); }); describe("object with parent hierarchy", () => { it("should return true when object and parent both have complete prerequisites", async () => { const parent = createMockObject("F-parent", undefined, ["F-prereq"]); const testObject = createMockObject("T-test", "F-parent", ["T-prereq"]); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById.mockResolvedValue(parent); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(2); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( parent, mockRepository, ); expect(mockRepository.getObjectById).toHaveBeenCalledWith("F-parent"); }); it("should return false when object has complete prerequisites but parent has incomplete prerequisites", async () => { const parent = createMockObject("F-parent", undefined, ["F-prereq"]); const testObject = createMockObject("T-test", "F-parent", ["T-prereq"]); mockCheckPrerequisitesComplete .mockResolvedValueOnce(true) // Object's prerequisites complete .mockResolvedValueOnce(false); // Parent's prerequisites incomplete mockRepository.getObjectById.mockResolvedValue(parent); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(false); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(2); expect(mockRepository.getObjectById).toHaveBeenCalledWith("F-parent"); }); it("should return false when object has incomplete prerequisites regardless of parent", async () => { const testObject = createMockObject("T-test", "F-parent", ["T-prereq"]); mockCheckPrerequisitesComplete.mockResolvedValue(false); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(false); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(1); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).not.toHaveBeenCalled(); }); }); describe("deep hierarchy", () => { it("should check prerequisites through multiple parent levels", async () => { const grandparent = createMockObject("E-grandparent", undefined, [ "E-prereq", ]); const parent = createMockObject("F-parent", "E-grandparent", [ "F-prereq", ]); const testObject = createMockObject("T-test", "F-parent", ["T-prereq"]); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById .mockResolvedValueOnce(parent) // First call for F-parent .mockResolvedValueOnce(grandparent); // Second call for E-grandparent const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(3); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( parent, mockRepository, ); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( grandparent, mockRepository, ); expect(mockRepository.getObjectById).toHaveBeenCalledWith("F-parent"); expect(mockRepository.getObjectById).toHaveBeenCalledWith( "E-grandparent", ); }); it("should return false when grandparent has incomplete prerequisites", async () => { const grandparent = createMockObject("E-grandparent", undefined, [ "E-prereq", ]); const parent = createMockObject("F-parent", "E-grandparent", [ "F-prereq", ]); const testObject = createMockObject("T-test", "F-parent", ["T-prereq"]); mockCheckPrerequisitesComplete .mockResolvedValueOnce(true) // Object's prerequisites complete .mockResolvedValueOnce(true) // Parent's prerequisites complete .mockResolvedValueOnce(false); // Grandparent's prerequisites incomplete mockRepository.getObjectById .mockResolvedValueOnce(parent) .mockResolvedValueOnce(grandparent); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(false); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(3); }); }); describe("missing parent handling", () => { it("should return true when parent object is not found", async () => { const testObject = createMockObject("T-test", "F-missing-parent"); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById.mockResolvedValue(null); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).toHaveBeenCalledWith( "F-missing-parent", ); }); it("should return true when getObjectById throws an error", async () => { const testObject = createMockObject("T-test", "F-error-parent"); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById.mockRejectedValue( new Error("Database error"), ); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); expect(result).toBe(true); expect(mockCheckPrerequisitesComplete).toHaveBeenCalledWith( testObject, mockRepository, ); expect(mockRepository.getObjectById).toHaveBeenCalledWith( "F-error-parent", ); }); }); describe("circular reference protection", () => { it("should prevent infinite loops with circular parent references", async () => { const objectA = createMockObject("T-object-a", "T-object-b"); const objectB = createMockObject("T-object-b", "T-object-a"); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById .mockResolvedValueOnce(objectB) // First call returns object B .mockResolvedValueOnce(objectA); // Second call returns object A const result = await checkHierarchicalPrerequisitesComplete( objectA, mockRepository, ); expect(result).toBe(true); // Should only check prerequisites for objectA and objectB once each expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(2); expect(mockRepository.getObjectById).toHaveBeenCalledTimes(2); }); it("should handle self-referencing objects", async () => { const selfReferencing = createMockObject("T-self", "T-self"); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById.mockResolvedValue(selfReferencing); const result = await checkHierarchicalPrerequisitesComplete( selfReferencing, mockRepository, ); expect(result).toBe(true); // Should only check prerequisites once due to circular reference protection expect(mockCheckPrerequisitesComplete).toHaveBeenCalledTimes(1); expect(mockRepository.getObjectById).toHaveBeenCalledWith("T-self"); }); }); describe("performance", () => { it("should complete within acceptable time for typical hierarchy", async () => { const grandparent = createMockObject("E-grandparent"); const parent = createMockObject("F-parent", "E-grandparent"); const testObject = createMockObject("T-test", "F-parent"); mockCheckPrerequisitesComplete.mockResolvedValue(true); mockRepository.getObjectById .mockResolvedValueOnce(parent) .mockResolvedValueOnce(grandparent); const startTime = Date.now(); const result = await checkHierarchicalPrerequisitesComplete( testObject, mockRepository, ); const endTime = Date.now(); expect(result).toBe(true); expect(endTime - startTime).toBeLessThan(100); // Should complete in less than 100ms }); }); });

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/langadventurellc/task-trellis-mcp'

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