Skip to main content
Glama

Task Trellis MCP

updateObject.test.ts31.2 kB
import { ServerConfig } from "../../../configuration"; import { TrellisObject, TrellisObjectPriority, TrellisObjectStatus, TrellisObjectType, } from "../../../models"; import { Repository } from "../../../repositories/Repository"; import { autoCompleteParentHierarchy, updateParentHierarchy, } from "../../../utils"; import { validateStatusTransition } from "../../../validation/validateStatusTransition"; import { updateObject } from "../updateObject"; // Mock the validateStatusTransition function jest.mock("../../../validation/validateStatusTransition", () => ({ validateStatusTransition: jest.fn(), })); // Mock the utils functions jest.mock("../../../utils", () => ({ updateParentHierarchy: jest.fn(), autoCompleteParentHierarchy: jest.fn(), })); const mockValidateStatusTransition = validateStatusTransition as jest.MockedFunction< typeof validateStatusTransition >; const mockUpdateParentHierarchy = updateParentHierarchy as jest.MockedFunction< typeof updateParentHierarchy >; const mockAutoCompleteParentHierarchy = autoCompleteParentHierarchy as jest.MockedFunction< typeof autoCompleteParentHierarchy >; describe("updateObject", () => { let mockRepository: jest.Mocked<Repository>; let mockServerConfig: jest.Mocked<ServerConfig>; beforeEach(() => { mockRepository = { getObjectById: jest.fn(), getObjects: jest.fn(), saveObject: jest.fn(), deleteObject: jest.fn(), getChildrenOf: jest.fn(), }; mockServerConfig = { mode: "local", autoCompleteParent: true, autoPrune: 0, }; jest.clearAllMocks(); }); const createMockObject = ( type: TrellisObjectType, overrides: Partial<TrellisObject> = {}, ): TrellisObject => ({ id: "T-test-task", type, title: "Test Task", status: TrellisObjectStatus.OPEN, priority: TrellisObjectPriority.MEDIUM, parent: "F-test-feature", prerequisites: [], affectedFiles: new Map(), log: [], schema: "1.0", childrenIds: [], body: "This is a test task", created: "2025-01-15T10:00:00Z", updated: "2025-01-15T10:00:00Z", ...overrides, }); describe("successful updates", () => { it("should update all properties of a task", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockValidateStatusTransition.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.HIGH, ["T-prereq-1", "T-prereq-2"], "Updated body content", TrellisObjectStatus.DRAFT, false, ); const expectedUpdatedObject = { ...mockTask, priority: TrellisObjectPriority.HIGH, prerequisites: ["T-prereq-1", "T-prereq-2"], body: "Updated body content", status: TrellisObjectStatus.DRAFT, }; expect(mockRepository.getObjectById).toHaveBeenCalledWith("T-test-task"); expect(mockValidateStatusTransition).toHaveBeenCalledWith( expectedUpdatedObject, mockRepository, ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expectedUpdatedObject, ); expect(result.content[0].text).toContain("Successfully updated object:"); }); it("should update only priority for a project", async () => { const mockProject = createMockObject(TrellisObjectType.PROJECT, { id: "P-test-project", }); mockRepository.getObjectById.mockResolvedValue(mockProject); mockRepository.saveObject.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "P-test-project", undefined, TrellisObjectPriority.LOW, ); const expectedUpdatedObject = { ...mockProject, priority: TrellisObjectPriority.LOW, }; expect(mockRepository.saveObject).toHaveBeenCalledWith( expectedUpdatedObject, ); expect(mockValidateStatusTransition).not.toHaveBeenCalled(); expect(result.content[0].text).toContain("Successfully updated object:"); }); it("should update only prerequisites for an epic", async () => { const mockEpic = createMockObject(TrellisObjectType.EPIC, { id: "E-test-epic", prerequisites: ["E-other-epic"], }); mockRepository.getObjectById.mockResolvedValue(mockEpic); mockRepository.saveObject.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "E-test-epic", undefined, undefined, ["E-new-prereq", "E-another-prereq"], ); const expectedUpdatedObject = { ...mockEpic, prerequisites: ["E-new-prereq", "E-another-prereq"], }; expect(mockRepository.saveObject).toHaveBeenCalledWith( expectedUpdatedObject, ); expect(result.content[0].text).toContain("Successfully updated object:"); }); it("should update only body content for a feature", async () => { const mockFeature = createMockObject(TrellisObjectType.FEATURE, { id: "F-test-feature", }); mockRepository.getObjectById.mockResolvedValue(mockFeature); mockRepository.saveObject.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "F-test-feature", undefined, undefined, undefined, "New detailed feature description", ); const expectedUpdatedObject = { ...mockFeature, body: "New detailed feature description", }; expect(mockRepository.saveObject).toHaveBeenCalledWith( expectedUpdatedObject, ); expect(result.content[0].text).toContain("Successfully updated object:"); }); it("should update only title for a task", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", "Updated Task Title", ); const expectedUpdatedObject = { ...mockTask, title: "Updated Task Title", }; expect(mockRepository.saveObject).toHaveBeenCalledWith( expectedUpdatedObject, ); expect(result.content[0].text).toContain("Successfully updated object:"); }); }); describe("priority updates", () => { it("should update priority to high", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.HIGH, ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ priority: TrellisObjectPriority.HIGH, }), ); }); it("should update priority to medium", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { priority: TrellisObjectPriority.HIGH, }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.MEDIUM, ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ priority: TrellisObjectPriority.MEDIUM, }), ); }); it("should update priority to low", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.LOW, ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ priority: TrellisObjectPriority.LOW, }), ); }); }); describe("prerequisites array updates", () => { it("should add prerequisites to empty array", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { prerequisites: [], }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, ["T-prereq-1", "T-prereq-2"], ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ prerequisites: ["T-prereq-1", "T-prereq-2"], }), ); }); it("should replace existing prerequisites", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { prerequisites: ["T-old-prereq"], }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, ["T-new-prereq-1", "T-new-prereq-2"], ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ prerequisites: ["T-new-prereq-1", "T-new-prereq-2"], }), ); }); it("should clear prerequisites with empty array", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { prerequisites: ["T-prereq-1", "T-prereq-2"], }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, [], ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ prerequisites: [], }), ); }); }); describe("status transitions", () => { it("should validate status transition from open to in-progress", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockValidateStatusTransition.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockValidateStatusTransition).toHaveBeenCalledWith( expect.objectContaining({ status: TrellisObjectStatus.IN_PROGRESS, }), mockRepository, ); expect(mockRepository.saveObject).toHaveBeenCalled(); }); it("should validate status transition from in-progress to done", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockValidateStatusTransition.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, ); expect(mockValidateStatusTransition).toHaveBeenCalledWith( expect.objectContaining({ status: TrellisObjectStatus.DONE, }), mockRepository, ); }); it("should bypass validation when force is true", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, true, ); expect(mockValidateStatusTransition).not.toHaveBeenCalled(); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ status: TrellisObjectStatus.IN_PROGRESS, }), ); }); }); describe("error handling", () => { it("should return error when object is not found", async () => { mockRepository.getObjectById.mockResolvedValue(null); const result = await updateObject( mockRepository, mockServerConfig, "T-nonexistent", undefined, TrellisObjectPriority.HIGH, ); expect(mockRepository.getObjectById).toHaveBeenCalledWith( "T-nonexistent", ); expect(mockRepository.saveObject).not.toHaveBeenCalled(); expect(mockValidateStatusTransition).not.toHaveBeenCalled(); expect(result.content[0].text).toBe( "Error: Object with ID 'T-nonexistent' not found", ); }); it("should return error when status validation fails", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); const validationError = new Error( "Cannot update status to 'in-progress' - prerequisites are not complete. Use force=true to override.", ); mockValidateStatusTransition.mockRejectedValue(validationError); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockValidateStatusTransition).toHaveBeenCalled(); expect(mockRepository.saveObject).not.toHaveBeenCalled(); expect(result.content[0].text).toBe( "Error updating object: Cannot update status to 'in-progress' - prerequisites are not complete. Use force=true to override.", ); }); it("should handle repository getObjectById errors gracefully", async () => { mockRepository.getObjectById.mockRejectedValue( new Error("Database connection failed"), ); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, TrellisObjectStatus.DONE, ); expect(result.content[0].text).toBe( "Error updating object: Database connection failed", ); }); it("should handle repository saveObject errors gracefully", async () => { const mockTask = createMockObject(TrellisObjectType.TASK); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockRejectedValue(new Error("Failed to save")); mockValidateStatusTransition.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, TrellisObjectStatus.DONE, ); expect(result.content[0].text).toBe( "Error updating object: Failed to save", ); }); it("should handle non-Error exceptions gracefully", async () => { mockRepository.getObjectById.mockRejectedValue("String error"); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.HIGH, ); expect(result.content[0].text).toBe( "Error updating object: String error", ); }); }); describe("edge cases and business logic", () => { it("should not update properties when they are undefined", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { priority: TrellisObjectPriority.HIGH, prerequisites: ["T-existing"], body: "Existing body", status: TrellisObjectStatus.IN_PROGRESS, }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, ); expect(mockRepository.saveObject).toHaveBeenCalledWith(mockTask); }); it("should handle complex update with multiple properties and status validation", async () => { const objectWithPrereqs = createMockObject(TrellisObjectType.TASK, { prerequisites: ["T-prereq-1"], priority: TrellisObjectPriority.LOW, body: "Original body", }); mockRepository.getObjectById.mockResolvedValue(objectWithPrereqs); mockRepository.saveObject.mockResolvedValue(); mockValidateStatusTransition.mockResolvedValue(); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.HIGH, ["T-prereq-1", "T-prereq-2", "T-prereq-3"], "Updated comprehensive body with new requirements", TrellisObjectStatus.IN_PROGRESS, false, ); const expectedObject = { ...objectWithPrereqs, priority: TrellisObjectPriority.HIGH, prerequisites: ["T-prereq-1", "T-prereq-2", "T-prereq-3"], body: "Updated comprehensive body with new requirements", status: TrellisObjectStatus.IN_PROGRESS, }; expect(mockValidateStatusTransition).toHaveBeenCalledWith( expectedObject, mockRepository, ); expect(mockRepository.saveObject).toHaveBeenCalledWith(expectedObject); expect(result.content[0].text).toContain("Successfully updated object:"); }); it("should preserve object structure and metadata during update", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { id: "T-preserve-test", title: "Important Task", parent: "F-important-feature", childrenIds: ["T-child-1", "T-child-2"], affectedFiles: new Map([["file1.ts", "description"]]), log: ["Previous log entry"], created: "2025-01-10T08:00:00Z", updated: "2025-01-14T12:00:00Z", schema: "1.0", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-preserve-test", undefined, TrellisObjectPriority.HIGH, ); expect(mockRepository.saveObject).toHaveBeenCalledWith( expect.objectContaining({ id: "T-preserve-test", title: "Important Task", parent: "F-important-feature", childrenIds: ["T-child-1", "T-child-2"], affectedFiles: new Map([["file1.ts", "description"]]), log: ["Previous log entry"], created: "2025-01-10T08:00:00Z", updated: "2025-01-14T12:00:00Z", schema: "1.0", priority: TrellisObjectPriority.HIGH, }), ); }); }); describe("parent hierarchy updates", () => { beforeEach(() => { mockUpdateParentHierarchy.mockClear(); mockAutoCompleteParentHierarchy.mockClear(); }); it("should call updateParentHierarchy when status changes to IN_PROGRESS", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockUpdateParentHierarchy.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockUpdateParentHierarchy).toHaveBeenCalledWith( "F-parent-feature", mockRepository, ); }); it("should not call updateParentHierarchy when status is already IN_PROGRESS", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockUpdateParentHierarchy).not.toHaveBeenCalled(); }); it("should not call updateParentHierarchy when status changes to something other than IN_PROGRESS", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, ); expect(mockUpdateParentHierarchy).not.toHaveBeenCalled(); }); it("should not call updateParentHierarchy when no status change is requested", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, TrellisObjectPriority.HIGH, ); expect(mockUpdateParentHierarchy).not.toHaveBeenCalled(); }); it("should handle updateParentHierarchy errors gracefully", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockUpdateParentHierarchy.mockRejectedValue( new Error("Parent hierarchy update failed"), ); const consoleSpy = jest.spyOn(console, "warn").mockImplementation(); const result = await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockUpdateParentHierarchy).toHaveBeenCalled(); expect(consoleSpy).toHaveBeenCalledWith( "Failed to update parent hierarchy:", expect.any(Error), ); expect(result.content[0].text).toContain("Successfully updated object:"); consoleSpy.mockRestore(); }); it("should not call updateParentHierarchy when object has no parent", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: null, }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockUpdateParentHierarchy).toHaveBeenCalledWith( null, mockRepository, ); }); it("should call autoCompleteParentHierarchy when status changes to DONE", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockAutoCompleteParentHierarchy.mockResolvedValue(); const serverConfig: ServerConfig = { mode: "local", autoCompleteParent: true, autoPrune: 0, }; await updateObject( mockRepository, serverConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, false, ); expect(mockAutoCompleteParentHierarchy).toHaveBeenCalledWith( mockRepository, expect.objectContaining({ status: TrellisObjectStatus.DONE, parent: "F-parent-feature", }), ); }); it("should call autoCompleteParentHierarchy when status changes to WONT_DO", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockAutoCompleteParentHierarchy.mockResolvedValue(); const serverConfig: ServerConfig = { mode: "local", autoCompleteParent: true, autoPrune: 0, }; await updateObject( mockRepository, serverConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.WONT_DO, false, ); expect(mockAutoCompleteParentHierarchy).toHaveBeenCalledWith( mockRepository, expect.objectContaining({ status: TrellisObjectStatus.WONT_DO, parent: "F-parent-feature", }), ); }); it("should not call autoCompleteParentHierarchy when status is already DONE", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.DONE, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, ); expect(mockAutoCompleteParentHierarchy).not.toHaveBeenCalled(); }); it("should not call autoCompleteParentHierarchy when status changes to something other than DONE or WONT_DO", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.OPEN, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); await updateObject( mockRepository, mockServerConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.IN_PROGRESS, ); expect(mockAutoCompleteParentHierarchy).not.toHaveBeenCalled(); }); it("should handle autoCompleteParentHierarchy errors gracefully", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockAutoCompleteParentHierarchy.mockRejectedValue( new Error("Auto-complete hierarchy update failed"), ); const consoleSpy = jest.spyOn(console, "warn").mockImplementation(); const serverConfig: ServerConfig = { mode: "local", autoCompleteParent: true, autoPrune: 0, }; const result = await updateObject( mockRepository, serverConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, false, ); expect(mockAutoCompleteParentHierarchy).toHaveBeenCalled(); expect(consoleSpy).toHaveBeenCalledWith( "Failed to auto-complete parent hierarchy:", expect.any(Error), ); expect(result.content[0].text).toContain("Successfully updated object:"); consoleSpy.mockRestore(); }); }); describe("serverConfig behavior", () => { it("should call autoCompleteParentHierarchy when serverConfig.autoCompleteParent is true", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); mockAutoCompleteParentHierarchy.mockResolvedValue(); const serverConfig: ServerConfig = { mode: "local", autoCompleteParent: true, autoPrune: 0, }; await updateObject( mockRepository, serverConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, false, ); expect(mockAutoCompleteParentHierarchy).toHaveBeenCalledWith( mockRepository, expect.objectContaining({ status: TrellisObjectStatus.DONE, parent: "F-parent-feature", }), ); }); it("should not call autoCompleteParentHierarchy when serverConfig.autoCompleteParent is false", async () => { const mockTask = createMockObject(TrellisObjectType.TASK, { status: TrellisObjectStatus.IN_PROGRESS, parent: "F-parent-feature", }); mockRepository.getObjectById.mockResolvedValue(mockTask); mockRepository.saveObject.mockResolvedValue(); const serverConfig: ServerConfig = { mode: "local", autoCompleteParent: false, autoPrune: 0, }; await updateObject( mockRepository, serverConfig, "T-test-task", undefined, undefined, undefined, undefined, TrellisObjectStatus.DONE, false, ); expect(mockAutoCompleteParentHierarchy).not.toHaveBeenCalled(); }); }); });

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