Add GitLab lifecycle automation for comments and source item state changes. - Add GitLab REST helpers for posting notes and closing or reopening issues and merge requests. - Register GitLab source-comment, tracking-comment, tracking-state, and source-close services with multi-project lifecycle wiring. - Add project settings, documentation, tests, and a changeset for GitLab completion comments and auto-close behavior. Files changed: .changeset/fn-7426-gitlab-lifecycle.md | 7 ++ docs/cli-reference.md | 2 +- docs/dashboard-guide.md | 3 +- docs/settings-reference.md | 5 +- packages/core/src/settings-schema.ts | 3 + packages/core/src/types.ts | 10 +++ .../src/__tests__/gitlab-issue-comment.test.ts | 37 +++++++++ .../__tests__/gitlab-source-issue-close.test.ts | 29 +++++++ .../src/__tests__/gitlab-tracking-comments.test.ts | 31 +++++++ .../src/__tests__/gitlab-tracking-state.test.ts | 42 ++++++++++ packages/dashboard/src/__tests__/gitlab.test.ts | 18 ++++ .../register-git-github.gitlab-lifecycle.test.ts | 54 ++++++++++++ packages/dashboard/src/gitlab-issue-comment.ts | 62 ++++++++++++++ packages/dashboard/src/gitlab-lifecycle.ts | 58 +++++++++++++ .../dashboard/src/gitlab-source-issue-close.ts | 45 ++++++++++ packages/dashboard/src/gitlab-tracking-comments.ts | 77 +++++++++++++++++ packages/dashboard/src/gitlab-tracking-state.ts | 97 ++++++++++++++++++++++ packages/dashboard/src/gitlab.ts | 30 +++++++ .../dashboard/src/routes/register-git-github.ts | 23 +++++ 19 files changed, 630 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-7426 Fusion-Task-Lineage: a46bd11f-96ef-4291-ab41-829007fa057a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
38 lines
2.5 KiB
TypeScript
38 lines
2.5 KiB
TypeScript
import { EventEmitter } from "node:events";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { GitLabIssueCommentService } from "../gitlab-issue-comment.js";
|
|
|
|
function jsonResponse(body: unknown, status = 200) { return new Response(JSON.stringify(body), { status, headers: { "Content-Type": "application/json" } }); }
|
|
function store(settings: any = {}) {
|
|
const emitter = new EventEmitter();
|
|
return Object.assign(emitter, {
|
|
getSettings: vi.fn().mockResolvedValue({ gitlabAuthToken: "token", gitlabInstanceUrl: "https://gitlab.example.com", gitlabCommentOnDone: true, ...settings }),
|
|
getGlobalSettingsStore: () => ({ getSettings: vi.fn().mockResolvedValue({}) }),
|
|
logEntry: vi.fn(),
|
|
});
|
|
}
|
|
const task: any = { id: "FN-1", title: "Fix", sourceIssue: { provider: "gitlab", repository: "g/p", issueNumber: 2, url: "https://gitlab.example.com/g/p/-/issues/2" }, gitlabTracking: { item: { kind: "project_issue", instanceUrl: "https://gitlab.example.com", host: "gitlab.example.com", url: "https://gitlab.example.com/g/p/-/issues/2", projectPath: "g/p", iid: 2, title: "Fix", state: "opened", linkedAt: "now" } } };
|
|
|
|
describe("GitLabIssueCommentService", () => {
|
|
beforeEach(() => vi.unstubAllGlobals());
|
|
it("posts source completion comments to GitLab project issues", async () => {
|
|
const fetchImpl = vi.fn().mockResolvedValue(jsonResponse({ id: 1 }));
|
|
vi.stubGlobal("fetch", fetchImpl);
|
|
const s = store();
|
|
new GitLabIssueCommentService(s as any).start();
|
|
s.emit("task:moved", { task, to: "done" });
|
|
await vi.waitFor(() => expect(fetchImpl).toHaveBeenCalled());
|
|
expect(fetchImpl.mock.calls[0][0]).toBe("https://gitlab.example.com/api/v4/projects/g%2Fp/issues/2/notes");
|
|
expect(s.logEntry).toHaveBeenCalledWith("FN-1", "Posted GitLab issue completion comment", "g/p#2");
|
|
});
|
|
it("skips non-GitLab and incomplete source metadata", async () => {
|
|
const fetchImpl = vi.fn(); vi.stubGlobal("fetch", fetchImpl);
|
|
const s = store(); new GitLabIssueCommentService(s as any).start();
|
|
s.emit("task:moved", { task: { ...task, sourceIssue: { provider: "github" } }, to: "done" });
|
|
s.emit("task:moved", { task: { id: "FN-2", sourceIssue: { provider: "gitlab" }, gitlabTracking: { item: { kind: "group_issue", iid: 3 } } }, to: "done" });
|
|
await new Promise((resolve) => setImmediate(resolve));
|
|
expect(fetchImpl).not.toHaveBeenCalled();
|
|
expect(s.logEntry).toHaveBeenCalledWith("FN-2", "Skipped GitLab source comment", "Linked GitLab source metadata is incomplete");
|
|
});
|
|
});
|