@pagopa/dx-savemoney 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/__tests__/finding.test.d.ts +17 -0
  2. package/dist/__tests__/finding.test.d.ts.map +1 -0
  3. package/dist/__tests__/finding.test.js +124 -0
  4. package/dist/__tests__/finding.test.js.map +1 -0
  5. package/dist/azure/__tests__/analyzer-tags.test.d.ts +8 -0
  6. package/dist/azure/__tests__/analyzer-tags.test.d.ts.map +1 -0
  7. package/dist/azure/__tests__/analyzer-tags.test.js +43 -0
  8. package/dist/azure/__tests__/analyzer-tags.test.js.map +1 -0
  9. package/dist/azure/__tests__/config.test.d.ts +9 -0
  10. package/dist/azure/__tests__/config.test.d.ts.map +1 -0
  11. package/dist/azure/__tests__/config.test.js +70 -0
  12. package/dist/azure/__tests__/config.test.js.map +1 -0
  13. package/dist/azure/__tests__/report.test.d.ts +9 -0
  14. package/dist/azure/__tests__/report.test.d.ts.map +1 -0
  15. package/dist/azure/__tests__/report.test.js +120 -0
  16. package/dist/azure/__tests__/report.test.js.map +1 -0
  17. package/dist/azure/__tests__/utils.test.d.ts +15 -0
  18. package/dist/azure/__tests__/utils.test.d.ts.map +1 -0
  19. package/dist/azure/__tests__/utils.test.js +181 -0
  20. package/dist/azure/__tests__/utils.test.js.map +1 -0
  21. package/dist/azure/analyzers/__tests__/advisor.test.d.ts +9 -0
  22. package/dist/azure/analyzers/__tests__/advisor.test.d.ts.map +1 -0
  23. package/dist/azure/analyzers/__tests__/advisor.test.js +314 -0
  24. package/dist/azure/analyzers/__tests__/advisor.test.js.map +1 -0
  25. package/dist/azure/resources/__tests__/storage.test.d.ts +11 -0
  26. package/dist/azure/resources/__tests__/storage.test.d.ts.map +1 -0
  27. package/dist/azure/resources/__tests__/storage.test.js +99 -0
  28. package/dist/azure/resources/__tests__/storage.test.js.map +1 -0
  29. package/dist/index.test.d.ts +2 -0
  30. package/dist/index.test.d.ts.map +1 -0
  31. package/dist/index.test.js +78 -0
  32. package/dist/index.test.js.map +1 -0
  33. package/package.json +3 -3
  34. package/src/azure/__tests__/report.test.ts +10 -8
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Tests for findingsFromAnalysisResult — verifies the adapter that converts
3
+ * a legacy `AnalysisResult.reason` string into a structured `Finding[]`.
4
+ *
5
+ * Behaviours covered:
6
+ * 1. Empty / whitespace-only reason → empty array.
7
+ * 2. Single sentence (with or without trailing period) → one finding.
8
+ * 3. Multi-sentence reason (". " separator) → one finding per sentence.
9
+ * 4. Trailing period on the whole string → not duplicated.
10
+ * 5. Custom `code` → propagated to every finding.
11
+ * 6. Omitted `code` → defaults to "custom.unknown".
12
+ * 7. Custom `source` → propagated.
13
+ * 8. Omitted `source` → defaults to "custom".
14
+ * 9. Every finding carries the correct `resourceId`, `severity`, `category`.
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=finding.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/finding.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Tests for findingsFromAnalysisResult — verifies the adapter that converts
3
+ * a legacy `AnalysisResult.reason` string into a structured `Finding[]`.
4
+ *
5
+ * Behaviours covered:
6
+ * 1. Empty / whitespace-only reason → empty array.
7
+ * 2. Single sentence (with or without trailing period) → one finding.
8
+ * 3. Multi-sentence reason (". " separator) → one finding per sentence.
9
+ * 4. Trailing period on the whole string → not duplicated.
10
+ * 5. Custom `code` → propagated to every finding.
11
+ * 6. Omitted `code` → defaults to "custom.unknown".
12
+ * 7. Custom `source` → propagated.
13
+ * 8. Omitted `source` → defaults to "custom".
14
+ * 9. Every finding carries the correct `resourceId`, `severity`, `category`.
15
+ */
16
+ import { describe, expect, it } from "vitest";
17
+ import { findingsFromAnalysisResult } from "../finding.js";
18
+ const BASE_ARGS = {
19
+ reason: "Low CPU usage.",
20
+ resourceId: "/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm",
21
+ severity: "high",
22
+ };
23
+ describe("findingsFromAnalysisResult", () => {
24
+ describe("empty / whitespace input", () => {
25
+ it("returns [] for an empty string", () => {
26
+ expect(findingsFromAnalysisResult({ ...BASE_ARGS, reason: "" })).toEqual([]);
27
+ });
28
+ it("returns [] for a whitespace-only string", () => {
29
+ expect(findingsFromAnalysisResult({ ...BASE_ARGS, reason: " " })).toEqual([]);
30
+ });
31
+ it("returns [] for a bare period", () => {
32
+ expect(findingsFromAnalysisResult({ ...BASE_ARGS, reason: "." })).toEqual([]);
33
+ });
34
+ });
35
+ describe("single sentence", () => {
36
+ it("returns one finding with a trailing period", () => {
37
+ const result = findingsFromAnalysisResult({
38
+ ...BASE_ARGS,
39
+ reason: "VM is deallocated.",
40
+ });
41
+ expect(result).toHaveLength(1);
42
+ expect(result[0].reason).toBe("VM is deallocated.");
43
+ });
44
+ it("appends a trailing period when missing", () => {
45
+ const result = findingsFromAnalysisResult({
46
+ ...BASE_ARGS,
47
+ reason: "VM is deallocated",
48
+ });
49
+ expect(result).toHaveLength(1);
50
+ expect(result[0].reason).toBe("VM is deallocated.");
51
+ });
52
+ });
53
+ describe("multi-sentence reason", () => {
54
+ it("splits on '. ' into separate findings", () => {
55
+ const result = findingsFromAnalysisResult({
56
+ ...BASE_ARGS,
57
+ reason: "VM is deallocated. No tags found. Low CPU usage.",
58
+ });
59
+ expect(result).toHaveLength(3);
60
+ expect(result[0].reason).toBe("VM is deallocated.");
61
+ expect(result[1].reason).toBe("No tags found.");
62
+ expect(result[2].reason).toBe("Low CPU usage.");
63
+ });
64
+ it("does not create an extra empty finding for a trailing period", () => {
65
+ const result = findingsFromAnalysisResult({
66
+ ...BASE_ARGS,
67
+ reason: "Sentence one. Sentence two.",
68
+ });
69
+ expect(result).toHaveLength(2);
70
+ });
71
+ });
72
+ describe("code field", () => {
73
+ it("uses the provided code for every finding", () => {
74
+ const result = findingsFromAnalysisResult({
75
+ ...BASE_ARGS,
76
+ code: "vm.deallocated",
77
+ reason: "A. B.",
78
+ });
79
+ expect(result.every((f) => f.code === "vm.deallocated")).toBe(true);
80
+ });
81
+ it("defaults code to 'custom.unknown' when omitted", () => {
82
+ const result = findingsFromAnalysisResult(BASE_ARGS);
83
+ expect(result[0].code).toBe("custom.unknown");
84
+ });
85
+ });
86
+ describe("source field", () => {
87
+ it("uses the provided source for every finding", () => {
88
+ const result = findingsFromAnalysisResult({
89
+ ...BASE_ARGS,
90
+ source: "advisor",
91
+ });
92
+ expect(result.every((f) => f.source === "advisor")).toBe(true);
93
+ });
94
+ it("defaults source to 'custom' when omitted", () => {
95
+ const result = findingsFromAnalysisResult(BASE_ARGS);
96
+ expect(result[0].source).toBe("custom");
97
+ });
98
+ });
99
+ describe("static fields on every finding", () => {
100
+ it("propagates resourceId to every finding", () => {
101
+ const result = findingsFromAnalysisResult({
102
+ ...BASE_ARGS,
103
+ reason: "A. B.",
104
+ });
105
+ expect(result.every((f) => f.resourceId === BASE_ARGS.resourceId)).toBe(true);
106
+ });
107
+ it("propagates severity to every finding", () => {
108
+ const result = findingsFromAnalysisResult({
109
+ ...BASE_ARGS,
110
+ reason: "A. B.",
111
+ severity: "medium",
112
+ });
113
+ expect(result.every((f) => f.severity === "medium")).toBe(true);
114
+ });
115
+ it("sets category to 'cost' on every finding", () => {
116
+ const result = findingsFromAnalysisResult({
117
+ ...BASE_ARGS,
118
+ reason: "A. B.",
119
+ });
120
+ expect(result.every((f) => f.category === "cost")).toBe(true);
121
+ });
122
+ });
123
+ });
124
+ //# sourceMappingURL=finding.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finding.test.js","sourceRoot":"","sources":["../../src/__tests__/finding.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Unit tests for tag-filter behavior on Advisor findings.
3
+ *
4
+ * These tests exercise the pure helper used by the Azure analyzer
5
+ * orchestrator to keep filtering semantics explicit and stable.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=analyzer-tags.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-tags.test.d.ts","sourceRoot":"","sources":["../../../src/azure/__tests__/analyzer-tags.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Unit tests for tag-filter behavior on Advisor findings.
3
+ *
4
+ * These tests exercise the pure helper used by the Azure analyzer
5
+ * orchestrator to keep filtering semantics explicit and stable.
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+ import { shouldIncludeAdvisorFindingForTags } from "../analyzer.js";
9
+ function mkFinding(resourceId, source) {
10
+ return {
11
+ category: "cost",
12
+ code: `${source}.test`,
13
+ reason: "Test finding.",
14
+ resourceId,
15
+ severity: "low",
16
+ source,
17
+ };
18
+ }
19
+ describe("shouldIncludeAdvisorFindingForTags", () => {
20
+ it("includes all findings when no tag filter is active", () => {
21
+ const finding = mkFinding("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1", "advisor");
22
+ expect(shouldIncludeAdvisorFindingForTags(finding, new Set(), false)).toBe(true);
23
+ });
24
+ it("keeps subscription-level Advisor findings global even with tag filters", () => {
25
+ const finding = mkFinding("/subscriptions/sub1", "advisor");
26
+ expect(shouldIncludeAdvisorFindingForTags(finding, new Set(), true)).toBe(true);
27
+ });
28
+ it("includes resource-level Advisor findings only when resource id is tag-matched", () => {
29
+ const finding = mkFinding("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm1", "advisor");
30
+ const taggedResourceIds = new Set([
31
+ "/subscriptions/sub1/resourcegroups/rg1/providers/microsoft.compute/virtualmachines/vm1",
32
+ ]);
33
+ expect(shouldIncludeAdvisorFindingForTags(finding, taggedResourceIds, true)).toBe(true);
34
+ });
35
+ it("excludes resource-level Advisor findings when resource id is not tag-matched", () => {
36
+ const finding = mkFinding("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm2", "advisor");
37
+ const taggedResourceIds = new Set([
38
+ "/subscriptions/sub1/resourcegroups/rg1/providers/microsoft.compute/virtualmachines/vm1",
39
+ ]);
40
+ expect(shouldIncludeAdvisorFindingForTags(finding, taggedResourceIds, true)).toBe(false);
41
+ });
42
+ });
43
+ //# sourceMappingURL=analyzer-tags.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer-tags.test.js","sourceRoot":"","sources":["../../../src/azure/__tests__/analyzer-tags.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAI9C,OAAO,EAAE,kCAAkC,EAAE,MAAM,gBAAgB,CAAC;AAEpE,SAAS,SAAS,CAAC,UAAkB,EAAE,MAAyB;IAC9D,OAAO;QACL,QAAQ,EAAE,MAAM;QAChB,IAAI,EAAE,GAAG,MAAM,OAAO;QACtB,MAAM,EAAE,eAAe;QACvB,UAAU;QACV,QAAQ,EAAE,KAAK;QACf,MAAM;KACP,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAClD,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,OAAO,GAAG,SAAS,CACvB,wFAAwF,EACxF,SAAS,CACV,CAAC;QAEF,MAAM,CACJ,kCAAkC,CAAC,OAAO,EAAE,IAAI,GAAG,EAAU,EAAE,KAAK,CAAC,CACtE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,OAAO,GAAG,SAAS,CAAC,qBAAqB,EAAE,SAAS,CAAC,CAAC;QAE5D,MAAM,CACJ,kCAAkC,CAAC,OAAO,EAAE,IAAI,GAAG,EAAU,EAAE,IAAI,CAAC,CACrE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+EAA+E,EAAE,GAAG,EAAE;QACvF,MAAM,OAAO,GAAG,SAAS,CACvB,wFAAwF,EACxF,SAAS,CACV,CAAC;QAEF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS;YACxC,wFAAwF;SACzF,CAAC,CAAC;QAEH,MAAM,CACJ,kCAAkC,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,CAAC,CACrE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,OAAO,GAAG,SAAS,CACvB,wFAAwF,EACxF,SAAS,CACV,CAAC;QAEF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAS;YACxC,wFAAwF;SACzF,CAAC,CAAC;QAEH,MAAM,CACJ,kCAAkC,CAAC,OAAO,EAAE,iBAAiB,EAAE,IAAI,CAAC,CACrE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests for loadConfig() — verifies that:
3
+ * 1. Returns default thresholds and prompts for subscriptionIds when no file is given.
4
+ * 2. Loads subscriptionIds, location, timespanDays and thresholds from a YAML file.
5
+ * 3. Partial threshold overrides keep defaults for non-overridden fields.
6
+ * 4. Throws a clear error for a non-existent explicit path.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.d.ts","sourceRoot":"","sources":["../../../src/azure/__tests__/config.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Tests for loadConfig() — verifies that:
3
+ * 1. Returns default thresholds and prompts for subscriptionIds when no file is given.
4
+ * 2. Loads subscriptionIds, location, timespanDays and thresholds from a YAML file.
5
+ * 3. Partial threshold overrides keep defaults for non-overridden fields.
6
+ * 4. Throws a clear error for a non-existent explicit path.
7
+ */
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { describe, expect, it } from "vitest";
11
+ import { loadConfig } from "../../index.js";
12
+ import { DEFAULT_THRESHOLDS } from "../../types.js";
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ /** Absolute paths used in tests */
15
+ const FIXTURE_PARTIAL = path.resolve(__dirname, "fixtures/partial-override.yaml");
16
+ const FIXTURE_FULL = path.resolve(__dirname, "fixtures/full-override.yaml");
17
+ describe("loadConfig", () => {
18
+ it("throws when explicit path does not exist", async () => {
19
+ await expect(loadConfig("/nonexistent/config.yaml")).rejects.toThrow("Config file not found");
20
+ });
21
+ it("loads subscriptionIds and defaults from a partial YAML file", async () => {
22
+ const result = await loadConfig(FIXTURE_PARTIAL);
23
+ expect(result.subscriptionIds).toEqual([
24
+ "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
25
+ ]);
26
+ expect(result.preferredLocation).toBe("italynorth");
27
+ expect(result.timespanDays).toBe(30);
28
+ });
29
+ it("loads partial threshold overrides and keeps defaults for missing fields", async () => {
30
+ const result = await loadConfig(FIXTURE_PARTIAL);
31
+ // Overridden values
32
+ expect(result.thresholds?.vm.cpuPercent).toBe(5);
33
+ expect(result.thresholds?.storage.transactionsPerDay).toBe(50);
34
+ // Non-overridden vm field keeps default
35
+ expect(result.thresholds?.vm.networkInBytesPerDay).toBe(DEFAULT_THRESHOLDS.vm.networkInBytesPerDay);
36
+ // Entire non-overridden sections keep defaults
37
+ expect(result.thresholds?.appService).toEqual(DEFAULT_THRESHOLDS.appService);
38
+ expect(result.thresholds?.containerApp).toEqual(DEFAULT_THRESHOLDS.containerApp);
39
+ expect(result.thresholds?.publicIp).toEqual(DEFAULT_THRESHOLDS.publicIp);
40
+ expect(result.thresholds?.staticSite).toEqual(DEFAULT_THRESHOLDS.staticSite);
41
+ });
42
+ it("loads all values from a full YAML file", async () => {
43
+ const result = await loadConfig(FIXTURE_FULL);
44
+ expect(result.subscriptionIds).toHaveLength(2);
45
+ expect(result.preferredLocation).toBe("westeurope");
46
+ expect(result.timespanDays).toBe(60);
47
+ expect(result.thresholds?.vm.cpuPercent).toBe(5);
48
+ expect(result.thresholds?.vm.networkInBytesPerDay).toBe(10485760);
49
+ expect(result.thresholds?.appService.cpuPercent).toBe(10);
50
+ expect(result.thresholds?.appService.memoryPercent).toBe(20);
51
+ expect(result.thresholds?.appService.premiumCpuPercent).toBe(15);
52
+ expect(result.thresholds?.containerApp.cpuNanoCores).toBe(5000000);
53
+ expect(result.thresholds?.containerApp.memoryBytes).toBe(52428800);
54
+ expect(result.thresholds?.containerApp.networkBytes).toBe(100000);
55
+ expect(result.thresholds?.storage.transactionsPerDay).toBe(50);
56
+ expect(result.thresholds?.publicIp.bytesInDDoS).toBe(1048576);
57
+ expect(result.thresholds?.staticSite.siteHits).toBe(500);
58
+ expect(result.thresholds?.staticSite.bytesSent).toBe(5242880);
59
+ });
60
+ it("returns a complete Thresholds object (all required keys present)", async () => {
61
+ const result = await loadConfig(FIXTURE_PARTIAL);
62
+ expect(result.thresholds).toHaveProperty("vm");
63
+ expect(result.thresholds).toHaveProperty("appService");
64
+ expect(result.thresholds).toHaveProperty("containerApp");
65
+ expect(result.thresholds).toHaveProperty("storage");
66
+ expect(result.thresholds).toHaveProperty("publicIp");
67
+ expect(result.thresholds).toHaveProperty("staticSite");
68
+ });
69
+ });
70
+ //# sourceMappingURL=config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../../src/azure/__tests__/config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Tests for generateReport() with the "lint" format.
3
+ *
4
+ * Since process.stdout.isTTY is false/undefined in test environments,
5
+ * ANSI color codes are empty strings (~no-ops), making the output
6
+ * easy to assert on plain text.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=report.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.test.d.ts","sourceRoot":"","sources":["../../../src/azure/__tests__/report.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Tests for generateReport() with the "lint" format.
3
+ *
4
+ * Since process.stdout.isTTY is false/undefined in test environments,
5
+ * ANSI color codes are empty strings (~no-ops), making the output
6
+ * easy to assert on plain text.
7
+ */
8
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
9
+ import { generateReport } from "../report.js";
10
+ // ── test fixtures ──────────────────────────────────────────────────────────
11
+ function makeEntry(id, costRisk, reason, suspectedUnused = true) {
12
+ return {
13
+ analysis: { costRisk, reason, suspectedUnused },
14
+ resource: {
15
+ id,
16
+ name: id.split("/").pop() ?? "unknown",
17
+ type: "Microsoft.Compute/virtualMachines",
18
+ },
19
+ };
20
+ }
21
+ const HIGH_ENTRY = makeEntry("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-high", "high", "VM is deallocated. No disk activity detected.");
22
+ const MEDIUM_ENTRY = makeEntry("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-medium", "medium", "Low CPU usage.");
23
+ const LOW_ENTRY = makeEntry("/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Compute/virtualMachines/vm-low", "low", "Possible idle resource.", false);
24
+ // ── helpers ────────────────────────────────────────────────────────────────
25
+ function allLogs(spy) {
26
+ return spy.mock.calls.map((c) => c[0]).join("\n");
27
+ }
28
+ // ── tests ──────────────────────────────────────────────────────────────────
29
+ describe("generateReport — lint format", () => {
30
+ let logSpy;
31
+ beforeEach(() => {
32
+ logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
33
+ });
34
+ afterEach(() => {
35
+ vi.restoreAllMocks();
36
+ });
37
+ it("prints the resource ID for each entry", async () => {
38
+ await generateReport([HIGH_ENTRY], "lint");
39
+ const output = allLogs(logSpy);
40
+ expect(output).toContain(HIGH_ENTRY.resource.id);
41
+ });
42
+ it("uses ✖ icon for high-risk resources", async () => {
43
+ await generateReport([HIGH_ENTRY], "lint");
44
+ const output = allLogs(logSpy);
45
+ expect(output).toContain("✖");
46
+ });
47
+ it("uses ⚠ icon for medium-risk resources", async () => {
48
+ await generateReport([MEDIUM_ENTRY], "lint");
49
+ const output = allLogs(logSpy);
50
+ expect(output).toContain("⚠");
51
+ });
52
+ it("uses ℹ icon for low-risk resources", async () => {
53
+ await generateReport([LOW_ENTRY], "lint");
54
+ const output = allLogs(logSpy);
55
+ expect(output).toContain("ℹ");
56
+ });
57
+ it("splits a multi-sentence reason into separate findings", async () => {
58
+ // Reason has two sentences separated by '. '
59
+ await generateReport([HIGH_ENTRY], "lint");
60
+ const calls = logSpy.mock.calls.map((c) => c[0] ?? "");
61
+ const findingLines = calls.filter((l) => l.startsWith(" "));
62
+ // "VM is deallocated." and "No disk activity detected." → 2 findings
63
+ expect(findingLines.length).toBe(2);
64
+ });
65
+ it("prints a Summary line at the end", async () => {
66
+ await generateReport([HIGH_ENTRY, MEDIUM_ENTRY, LOW_ENTRY], "lint");
67
+ const output = allLogs(logSpy);
68
+ expect(output).toContain("Summary:");
69
+ });
70
+ it("summary reports correct total issue count", async () => {
71
+ await generateReport([HIGH_ENTRY, MEDIUM_ENTRY, LOW_ENTRY], "lint");
72
+ const output = allLogs(logSpy);
73
+ // HIGH has 2 findings (split reason), MEDIUM and LOW have 1 each → total 4
74
+ expect(output).toContain("4 issues found");
75
+ });
76
+ it("summary reports correctly with a single issue (no plural)", async () => {
77
+ await generateReport([MEDIUM_ENTRY], "lint");
78
+ const output = allLogs(logSpy);
79
+ // "1 issue found" (not "1 issues found")
80
+ expect(output).toContain("1 issue found");
81
+ });
82
+ it("shows risk level label in uppercase for each finding", async () => {
83
+ await generateReport([HIGH_ENTRY], "lint");
84
+ const output = allLogs(logSpy);
85
+ expect(output).toContain("HIGH");
86
+ });
87
+ it("prints nothing but the summary for an empty report", async () => {
88
+ await generateReport([], "lint");
89
+ const calls = logSpy.mock.calls.map((c) => c[0]);
90
+ expect(calls).toHaveLength(1);
91
+ expect(calls[0]).toContain("0 issues found");
92
+ });
93
+ });
94
+ describe("generateReport — table format", () => {
95
+ let logSpy;
96
+ beforeEach(() => {
97
+ logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined);
98
+ });
99
+ afterEach(() => {
100
+ vi.restoreAllMocks();
101
+ });
102
+ it("renders without throwing for an empty report", async () => {
103
+ await expect(generateReport([], "table")).resolves.toBeUndefined();
104
+ // Table is always printed; the summary line ("0 issues found") follows.
105
+ expect(logSpy.mock.calls.length).toBeGreaterThanOrEqual(1);
106
+ const output = logSpy.mock.calls
107
+ .map((c) => String(c[0]))
108
+ .join("\n");
109
+ expect(output).toContain("0 issues found");
110
+ });
111
+ it("includes resource name and reason in the rendered table", async () => {
112
+ await generateReport([HIGH_ENTRY], "table");
113
+ const output = logSpy.mock.calls
114
+ .map((c) => String(c[0]))
115
+ .join("\n");
116
+ expect(output).toContain("vm-high");
117
+ expect(output).toContain("VM is deallocated");
118
+ });
119
+ });
120
+ //# sourceMappingURL=report.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.test.js","sourceRoot":"","sources":["../../../src/azure/__tests__/report.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tests for matchesTags() — verifies AND-logic tag filtering:
3
+ * 1. No filter (undefined/empty) → always include the resource.
4
+ * 2. Exact key-value match → include.
5
+ * 3. Key missing on resource → exclude.
6
+ * 4. Key present but wrong value → exclude.
7
+ * 5. Multiple tags: all match → include; any mismatch → exclude.
8
+ *
9
+ * Tests for getMetric() cache behaviour:
10
+ * 6. resetMetricsCache() clears state between runs.
11
+ * 7. Concurrent calls for the same key coalesce into one network call.
12
+ * 8. A failed call is cached as a fulfilled null result (not retried silently).
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=utils.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../../src/azure/__tests__/utils.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}