78 lines
2.4 KiB
TypeScript
78 lines
2.4 KiB
TypeScript
import { Effect } from "effect";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
decodeProjectIssueRequest,
|
|
ProjectIssueTransitionError,
|
|
ProjectIssueValidationError,
|
|
projectIssueDraftFromSignal,
|
|
queueProjectIssue,
|
|
validateProjectIssueDraft,
|
|
} from "./project-issue";
|
|
|
|
describe("project issue primitives", () => {
|
|
it("normalizes an explicit project request", () => {
|
|
const request = Effect.runSync(
|
|
decodeProjectIssueRequest({
|
|
body: " Investigate the staging failure. ",
|
|
kind: "explicit",
|
|
projectId: "project-1",
|
|
title: " Staging failure ",
|
|
})
|
|
);
|
|
|
|
expect(request).toEqual({
|
|
body: "Investigate the staging failure.",
|
|
kind: "explicit",
|
|
projectId: "project-1",
|
|
title: "Staging failure",
|
|
});
|
|
});
|
|
|
|
it("rejects invalid issue drafts before persistence", () => {
|
|
const error = Effect.runSync(
|
|
Effect.flip(
|
|
validateProjectIssueDraft({
|
|
body: "too short",
|
|
title: "No",
|
|
})
|
|
)
|
|
);
|
|
|
|
expect(error).toBeInstanceOf(ProjectIssueValidationError);
|
|
expect(error.reason).toBe("TitleTooShort");
|
|
});
|
|
|
|
it("projects a project Signal into the existing issue shape", () => {
|
|
const draft = Effect.runSync(
|
|
projectIssueDraftFromSignal({
|
|
problemStatement: {
|
|
constraints: ["Do not change production"],
|
|
desiredOutcome: "Staging deploys successfully",
|
|
summary: "The deploy fails during DNS resolution",
|
|
title: "Fix staging deploy DNS failure",
|
|
},
|
|
signalId: "signal-1",
|
|
})
|
|
);
|
|
|
|
expect(draft.title).toBe("Fix staging deploy DNS failure");
|
|
expect(draft.body).toContain("The deploy fails during DNS resolution");
|
|
expect(draft.body).toContain("Source Signal: signal-1");
|
|
});
|
|
|
|
it("queues retryable issue states and preserves active states", () => {
|
|
expect(Effect.runSync(queueProjectIssue("open"))).toBe("queued");
|
|
expect(Effect.runSync(queueProjectIssue("failed"))).toBe("queued");
|
|
expect(Effect.runSync(queueProjectIssue("needs-input"))).toBe("queued");
|
|
expect(Effect.runSync(queueProjectIssue("working"))).toBe("working");
|
|
});
|
|
|
|
it("does not reopen completed issues", () => {
|
|
const error = Effect.runSync(Effect.flip(queueProjectIssue("completed")));
|
|
|
|
expect(error).toBeInstanceOf(ProjectIssueTransitionError);
|
|
expect(error.reason).toBe("CompletedIssue");
|
|
});
|
|
});
|