Files
prowler/ui/lib/findings-groups.test.ts
T
Alan Buscaglia 577aa14acc fix(ui): correct IaC findings counters (#10736)
Co-authored-by: alejandrobailo <alejandrobailo94@gmail.com>
2026-04-17 12:48:57 +02:00

354 lines
8.6 KiB
TypeScript

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>): 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");
});
});