Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 89 additions & 1 deletion test/unit/linked-issue-hard-rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
resolveLinkedIssueHardRule,
resolveLinkedIssueHasOpenReference,
type LinkedIssueFacts,
type LinkedIssueHardRulesConfig,

Check notice on line 14 in test/unit/linked-issue-hard-rules.test.ts

View check run for this annotation

Loopover ORB / LoopOver Context

Review queue is busy

This repo has a busy review queue in the local Gittensory cache.
} from "../../src/review/linked-issue-hard-rules";
import type { LinkedIssueFactsFetch } from "../../src/github/backfill";
import { normalizeLinkedIssueHardRulesConfig } from "../../src/review/linked-issue-hard-rules-config";
import { isLinkedIssueHardRuleMode, normalizeLinkedIssueHardRulesConfig } from "../../src/review/linked-issue-hard-rules-config";
import { parseFocusManifest, resolveEffectiveSettings } from "../../src/signals/focus-manifest";
import { setLocalManifestReader } from "../../src/signals/focus-manifest-loader";
import type { RepositorySettings } from "../../src/types";
Expand Down Expand Up @@ -370,6 +370,94 @@
expect(normalizeLinkedIssueHardRulesConfig([], warnings)).toEqual(DEFAULT_LINKED_ISSUE_HARD_RULES);
expect(warnings).toEqual(["settings.linkedIssueHardRules must be an object; using the default all-off policy."]);
});

// The src/ twin's own normalizer branches (#5845): the engine copy has full coverage via
// linked-issue-hard-rules-config-engine.test.ts; these mirror the still-uncovered src-side arms.
it("isLinkedIssueHardRuleMode accepts the valid modes and rejects everything else", () => {
expect(isLinkedIssueHardRuleMode("block")).toBe(true);
expect(isLinkedIssueHardRuleMode("off")).toBe(true);
expect(isLinkedIssueHardRuleMode("warn")).toBe(false);
expect(isLinkedIssueHardRuleMode(123)).toBe(false);
expect(isLinkedIssueHardRuleMode(undefined)).toBe(false);
});

it("returns the all-off default (no warning) for undefined input, and warns for string/null input", () => {
const undefWarnings: string[] = [];
expect(normalizeLinkedIssueHardRulesConfig(undefined, undefWarnings)).toEqual({
...DEFAULT_LINKED_ISSUE_HARD_RULES,
pointBearingLabels: [],
maintainerOnlyLabels: [],
});
expect(undefWarnings).toEqual([]);

for (const bad of ["nope", null as unknown]) {
const warnings: string[] = [];
expect(normalizeLinkedIssueHardRulesConfig(bad, warnings)).toEqual({
...DEFAULT_LINKED_ISSUE_HARD_RULES,
pointBearingLabels: [],
maintainerOnlyLabels: [],
});
expect(warnings.some((w) => w.includes("must be an object"))).toBe(true);
}
});

it("parses a fully valid object, trimming labels and keeping every provided value", () => {
const warnings: string[] = [];
const result = normalizeLinkedIssueHardRulesConfig(
{
ownerAssignedClose: "block",
assignedIssueClose: "off",
missingPointLabelClose: "block",
maintainerOnlyLabelClose: "block",
pointBearingLabels: [" points ", "size"],
maintainerOnlyLabels: ["maintainer"],
defaultLabelRepo: true,
verifyBeforeClose: false,
closeDelaySeconds: 90,
},
warnings,
);
expect(result).toEqual({
ownerAssignedClose: "block",
assignedIssueClose: "off",
missingPointLabelClose: "block",
maintainerOnlyLabelClose: "block",
pointBearingLabels: ["points", "size"],
maintainerOnlyLabels: ["maintainer"],
defaultLabelRepo: true,
verifyBeforeClose: false,
closeDelaySeconds: 90,
});
expect(warnings).toEqual([]);
});

it("warns on an invalid mode and falls back to the field default", () => {
const warnings: string[] = [];
const result = normalizeLinkedIssueHardRulesConfig({ ownerAssignedClose: "sometimes" }, warnings);
expect(result.ownerAssignedClose).toBe("off");
expect(warnings.some((w) => w.includes("ownerAssignedClose"))).toBe(true);
});

it("warns on a non-boolean flag and falls back to the field default", () => {
const warnings: string[] = [];
const result = normalizeLinkedIssueHardRulesConfig({ defaultLabelRepo: "yes", verifyBeforeClose: 0 }, warnings);
expect(result.defaultLabelRepo).toBe(false);
expect(result.verifyBeforeClose).toBe(true);
expect(warnings.filter((w) => w.includes("must be a boolean")).length).toBe(2);
});

it("floors and clamps a valid closeDelaySeconds, and warns + defaults on invalid values", () => {
expect(normalizeLinkedIssueHardRulesConfig({ closeDelaySeconds: 10.9 }, []).closeDelaySeconds).toBe(10);
expect(normalizeLinkedIssueHardRulesConfig({ closeDelaySeconds: 5000 }, []).closeDelaySeconds).toBe(300);

const negative: string[] = [];
expect(normalizeLinkedIssueHardRulesConfig({ closeDelaySeconds: -1 }, negative).closeDelaySeconds).toBe(30);
expect(negative.some((w) => w.includes("closeDelaySeconds"))).toBe(true);

const nan: string[] = [];
expect(normalizeLinkedIssueHardRulesConfig({ closeDelaySeconds: Number.NaN }, nan).closeDelaySeconds).toBe(30);
expect(nan.some((w) => w.includes("closeDelaySeconds"))).toBe(true);
});
});

describe("resolveLinkedIssueHardRule (#1144 — overflow + orchestration)", () => {
Expand Down
Loading