import { describe, expect, it } from "vitest"; import type { FindingGroupRow } from "@/types"; import { canDrillDownFindingGroup, getActiveStatusFilter, getFilteredFindingGroupDelta, getFindingGroupDelta, getFindingGroupImpactedCounts, isFindingGroupMuted, } from "./findings-groups"; function makeGroup(overrides?: Partial): FindingGroupRow { return { id: "group-1", rowType: "group", checkId: "check-1", checkTitle: "Test finding group", severity: "high", status: "FAIL", muted: false, resourcesTotal: 5, resourcesFail: 3, newCount: 0, changedCount: 0, mutedCount: 0, providers: ["aws"], updatedAt: "2026-04-10T00:00:00Z", ...overrides, }; } describe("isFindingGroupMuted", () => { it("should prefer the explicit muted flag from the API", () => { expect( isFindingGroupMuted( makeGroup({ muted: true, mutedCount: 0, resourcesFail: 3, resourcesTotal: 5, }), ), ).toBe(true); }); it("should fall back to legacy counters when muted is not available", () => { expect( isFindingGroupMuted( makeGroup({ muted: undefined, mutedCount: 3, resourcesFail: 3, }), ), ).toBe(true); }); }); describe("getFindingGroupDelta", () => { it("should return new when the muted breakdown contains new findings", () => { expect( getFindingGroupDelta( makeGroup({ newCount: 0, changedCount: 0, newFailMutedCount: 1, }), ), ).toBe("new"); }); it("should return changed when only changed breakdown counters are present", () => { expect( getFindingGroupDelta( makeGroup({ newCount: 0, changedCount: 0, changedManualMutedCount: 2, }), ), ).toBe("changed"); }); it("should prioritize new over changed when both breakdowns are present", () => { expect( getFindingGroupDelta( makeGroup({ newCount: 0, changedCount: 0, newPassCount: 1, changedFailCount: 3, }), ), ).toBe("new"); }); it("should fall back to legacy counters when breakdowns are missing", () => { expect( getFindingGroupDelta( makeGroup({ newCount: 1, changedCount: 0, }), ), ).toBe("new"); }); }); describe("getActiveStatusFilter", () => { it("returns null when no status filter is active", () => { expect(getActiveStatusFilter({})).toBeNull(); }); it("returns the single value from filter[status]", () => { const result = getActiveStatusFilter({ "filter[status]": "FAIL" }); expect(result).toEqual(new Set(["FAIL"])); }); it("returns the parsed set from filter[status__in]", () => { const result = getActiveStatusFilter({ "filter[status__in]": "FAIL,MANUAL", }); expect(result).toEqual(new Set(["FAIL", "MANUAL"])); }); it("prefers filter[status] over filter[status__in] when both are present", () => { const result = getActiveStatusFilter({ "filter[status]": "PASS", "filter[status__in]": "FAIL,MANUAL", }); expect(result).toEqual(new Set(["PASS"])); }); it("ignores unknown status values and returns null if nothing remains", () => { expect( getActiveStatusFilter({ "filter[status__in]": "UNKNOWN,FOO" }), ).toBeNull(); }); }); describe("getFindingGroupImpactedCounts", () => { it("should fall back to pass and fail counts when resources total is zero", () => { // Given const group = makeGroup({ resourcesTotal: 0, resourcesFail: 0, failCount: 3, passCount: 2, muted: false, }); // When const result = getFindingGroupImpactedCounts(group); // Then expect(result).toEqual({ impacted: 3, total: 5 }); }); it("should include manual findings in fallback counts when resources total is zero", () => { // Given const group = makeGroup({ resourcesTotal: 0, resourcesFail: 0, failCount: 3, passCount: 2, manualCount: 4, muted: false, }); // When const result = getFindingGroupImpactedCounts(group); // Then expect(result).toEqual({ impacted: 3, total: 9 }); }); it("should include muted pass and fail counts in the denominator when the result is muted", () => { // Given const group = makeGroup({ resourcesTotal: 0, resourcesFail: 0, failCount: 3, passCount: 2, failMutedCount: 4, passMutedCount: 1, muted: true, }); // When const result = getFindingGroupImpactedCounts(group); // Then expect(result).toEqual({ impacted: 3, total: 10 }); }); it("should keep resource-based counts when resources total is available", () => { // Given const group = makeGroup({ resourcesTotal: 6, resourcesFail: 4, failCount: 2, passCount: 1, failMutedCount: 5, passMutedCount: 3, muted: true, }); // When const result = getFindingGroupImpactedCounts(group); // Then expect(result).toEqual({ impacted: 4, total: 6 }); }); }); describe("canDrillDownFindingGroup", () => { it("should allow drill-down when resources exist", () => { expect( canDrillDownFindingGroup( makeGroup({ resourcesTotal: 2, failCount: 0, }), ), ).toBe(true); }); it("should keep zero-resource fallback groups non-expandable even when fallback counts are present", () => { expect( canDrillDownFindingGroup( makeGroup({ resourcesTotal: 0, failCount: 0, passCount: 2, manualCount: 1, }), ), ).toBe(false); }); it("should keep drill-down disabled for zero-resource groups when the displayed total is zero", () => { expect( canDrillDownFindingGroup( makeGroup({ resourcesTotal: 0, failCount: 0, passCount: 0, }), ), ).toBe(false); }); }); describe("getFilteredFindingGroupDelta", () => { it("falls back to the aggregate delta when no status filter is active", () => { expect( getFilteredFindingGroupDelta( makeGroup({ newPassCount: 2, }), {}, ), ).toBe("new"); }); it("ignores deltas that belong to filtered-out statuses", () => { // Filter is FAIL, but the only delta is a new PASS → should be hidden. expect( getFilteredFindingGroupDelta( makeGroup({ newPassCount: 3, }), { "filter[status__in]": "FAIL" }, ), ).toBe("none"); }); it("surfaces FAIL deltas when the filter is FAIL", () => { expect( getFilteredFindingGroupDelta( makeGroup({ newFailCount: 1, }), { "filter[status]": "FAIL" }, ), ).toBe("new"); }); it("counts muted breakdown counters towards the filtered status", () => { // A muted new FAIL still belongs to the FAIL bucket — a FAIL filter // should still light up the "new" indicator. expect( getFilteredFindingGroupDelta( makeGroup({ newFailMutedCount: 1, }), { "filter[status]": "FAIL" }, ), ).toBe("new"); }); it("sums multiple filtered statuses from filter[status__in]", () => { // Filter is FAIL+MANUAL, new delta is only in MANUAL → should still show. expect( getFilteredFindingGroupDelta( makeGroup({ newManualCount: 1, }), { "filter[status__in]": "FAIL,MANUAL" }, ), ).toBe("new"); }); it("prefers new over changed within the filtered status", () => { expect( getFilteredFindingGroupDelta( makeGroup({ newFailCount: 1, changedFailCount: 2, }), { "filter[status]": "FAIL" }, ), ).toBe("new"); }); it("returns changed when only changed counters match the filtered status", () => { expect( getFilteredFindingGroupDelta( makeGroup({ newPassCount: 2, // filtered out changedFailCount: 1, }), { "filter[status]": "FAIL" }, ), ).toBe("changed"); }); it("falls back to the aggregate delta when breakdowns are missing (legacy API)", () => { // No breakdown fields populated but legacy newCount is set. With a FAIL // filter active we cannot know which status bucket it belongs to, so we // fall back to showing the delta rather than silently hiding it. expect( getFilteredFindingGroupDelta( makeGroup({ newCount: 1, }), { "filter[status]": "FAIL" }, ), ).toBe("new"); }); });