112 lines
3.7 KiB
JavaScript
112 lines
3.7 KiB
JavaScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
isStaleNeedDetails,
|
|
isStaleDefer,
|
|
detectClosedExternally,
|
|
} from "../../../scripts/features/lib/lifecycle.mjs";
|
|
|
|
const now = new Date("2026-05-19T00:00:00Z");
|
|
|
|
describe("isStaleNeedDetails", () => {
|
|
it("returns stale info when author silent ≥30 days", () => {
|
|
const issue = {
|
|
author: { login: "alice" },
|
|
createdAt: new Date(now.getTime() - 60 * 86400_000).toISOString(),
|
|
comments: [
|
|
{
|
|
author: { login: "alice" },
|
|
createdAt: new Date(now.getTime() - 40 * 86400_000).toISOString(),
|
|
},
|
|
{
|
|
author: { login: "bob" },
|
|
createdAt: new Date(now.getTime() - 5 * 86400_000).toISOString(),
|
|
},
|
|
],
|
|
};
|
|
const r = isStaleNeedDetails(issue, 30, now);
|
|
assert.equal(r.stale, true);
|
|
assert.equal(r.daysSilent, 40);
|
|
assert.equal(r.lastAuthorActivity, issue.comments[0].createdAt);
|
|
});
|
|
|
|
it("returns not stale when author replied within 30 days", () => {
|
|
const issue = {
|
|
author: { login: "alice" },
|
|
createdAt: new Date(now.getTime() - 60 * 86400_000).toISOString(),
|
|
comments: [
|
|
{
|
|
author: { login: "alice" },
|
|
createdAt: new Date(now.getTime() - 10 * 86400_000).toISOString(),
|
|
},
|
|
],
|
|
};
|
|
const r = isStaleNeedDetails(issue, 30, now);
|
|
assert.equal(r.stale, false);
|
|
});
|
|
|
|
it("uses createdAt when author never commented again", () => {
|
|
const issue = {
|
|
author: { login: "alice" },
|
|
createdAt: new Date(now.getTime() - 40 * 86400_000).toISOString(),
|
|
comments: [
|
|
{
|
|
author: { login: "bob" },
|
|
createdAt: new Date(now.getTime() - 5 * 86400_000).toISOString(),
|
|
},
|
|
],
|
|
};
|
|
const r = isStaleNeedDetails(issue, 30, now);
|
|
assert.equal(r.stale, true);
|
|
assert.equal(r.daysSilent, 40);
|
|
});
|
|
});
|
|
|
|
describe("isStaleDefer", () => {
|
|
it("stale when classified_at ≥90 days ago", () => {
|
|
const meta = {
|
|
snapshot: { classified_at: new Date(now.getTime() - 100 * 86400_000).toISOString() },
|
|
};
|
|
const r = isStaleDefer(meta, 90, now);
|
|
assert.equal(r.stale, true);
|
|
assert.equal(r.daysInDefer, 100);
|
|
});
|
|
it("not stale when classified_at <90 days", () => {
|
|
const meta = {
|
|
snapshot: { classified_at: new Date(now.getTime() - 60 * 86400_000).toISOString() },
|
|
};
|
|
const r = isStaleDefer(meta, 90, now);
|
|
assert.equal(r.stale, false);
|
|
});
|
|
it("uses fallback mtime when classified_at missing", () => {
|
|
const meta = { snapshot: {} };
|
|
const fallback = new Date(now.getTime() - 100 * 86400_000);
|
|
const r = isStaleDefer(meta, 90, now, fallback);
|
|
assert.equal(r.stale, true);
|
|
});
|
|
});
|
|
|
|
describe("detectClosedExternally", () => {
|
|
it("flags as closed_externally when issue closed but snapshot says open", () => {
|
|
const issue = { state: "CLOSED", closedAt: "2026-05-01T00:00:00Z", stateReason: "NOT_PLANNED" };
|
|
const meta = { snapshot: { state: "open" } };
|
|
const r = detectClosedExternally(issue, meta);
|
|
assert.equal(r.flagged, true);
|
|
assert.equal(r.stateReason, "NOT_PLANNED");
|
|
});
|
|
|
|
it("emits delivery warning when COMPLETED reason", () => {
|
|
const issue = { state: "CLOSED", closedAt: "2026-05-01T00:00:00Z", stateReason: "COMPLETED" };
|
|
const meta = { snapshot: { state: "open" } };
|
|
const r = detectClosedExternally(issue, meta);
|
|
assert.equal(r.flagged, true);
|
|
assert.equal(r.warningMessage, "possibly delivered by external work");
|
|
});
|
|
|
|
it("returns flagged=false when issue still open", () => {
|
|
const issue = { state: "OPEN" };
|
|
const meta = { snapshot: { state: "open" } };
|
|
assert.equal(detectClosedExternally(issue, meta).flagged, false);
|
|
});
|
|
});
|