@pagopa/dx-savemoney 0.6.1 → 0.6.2
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.
- package/dist/azure/pricing/pricing-service.d.ts.map +1 -1
- package/dist/azure/pricing/pricing-service.js +6 -1
- package/dist/azure/pricing/pricing-service.js.map +1 -1
- package/dist/azure/pricing/schema.d.ts.map +1 -1
- package/dist/azure/pricing/schema.js +22 -14
- package/dist/azure/pricing/schema.js.map +1 -1
- package/dist/azure/resources/app-service.d.ts.map +1 -1
- package/dist/azure/resources/app-service.js +2 -2
- package/dist/azure/resources/app-service.js.map +1 -1
- package/dist/azure/resources/container-app.d.ts.map +1 -1
- package/dist/azure/resources/container-app.js +1 -1
- package/dist/azure/resources/container-app.js.map +1 -1
- package/dist/azure/resources/disk.js +3 -1
- package/dist/azure/resources/disk.js.map +1 -1
- package/dist/azure/resources/nic.js +3 -1
- package/dist/azure/resources/nic.js.map +1 -1
- package/dist/azure/resources/private-endpoint.d.ts.map +1 -1
- package/dist/azure/resources/private-endpoint.js +1 -1
- package/dist/azure/resources/private-endpoint.js.map +1 -1
- package/dist/azure/resources/public-ip.d.ts.map +1 -1
- package/dist/azure/resources/public-ip.js +4 -2
- package/dist/azure/resources/public-ip.js.map +1 -1
- package/dist/azure/resources/static-web-app.d.ts.map +1 -1
- package/dist/azure/resources/static-web-app.js +1 -1
- package/dist/azure/resources/static-web-app.js.map +1 -1
- package/dist/azure/resources/vm.js +6 -2
- package/dist/azure/resources/vm.js.map +1 -1
- package/dist/azure/utils.d.ts.map +1 -1
- package/dist/azure/utils.js +7 -2
- package/dist/azure/utils.js.map +1 -1
- package/package.json +1 -8
- package/src/azure/pricing/__tests__/schema.test.ts +30 -0
- package/src/azure/pricing/pricing-service.ts +6 -3
- package/src/azure/pricing/schema.ts +22 -14
- package/src/azure/resources/app-service.ts +4 -2
- package/src/azure/resources/container-app.ts +2 -1
- package/src/azure/resources/disk.ts +3 -3
- package/src/azure/resources/nic.ts +3 -3
- package/src/azure/resources/private-endpoint.ts +2 -1
- package/src/azure/resources/public-ip.ts +5 -4
- package/src/azure/resources/static-web-app.ts +2 -1
- package/src/azure/resources/vm.ts +6 -6
- package/src/azure/utils.ts +8 -2
- package/dist/__tests__/finding.test.d.ts +0 -17
- package/dist/__tests__/finding.test.d.ts.map +0 -1
- package/dist/__tests__/finding.test.js +0 -146
- package/dist/__tests__/finding.test.js.map +0 -1
- package/dist/azure/__tests__/analyzer-tags.test.d.ts +0 -8
- package/dist/azure/__tests__/analyzer-tags.test.d.ts.map +0 -1
- package/dist/azure/__tests__/analyzer-tags.test.js +0 -70
- package/dist/azure/__tests__/analyzer-tags.test.js.map +0 -1
- package/dist/azure/__tests__/azqr.test.d.ts +0 -13
- package/dist/azure/__tests__/azqr.test.d.ts.map +0 -1
- package/dist/azure/__tests__/azqr.test.js +0 -177
- package/dist/azure/__tests__/azqr.test.js.map +0 -1
- package/dist/azure/__tests__/config.test.d.ts +0 -9
- package/dist/azure/__tests__/config.test.d.ts.map +0 -1
- package/dist/azure/__tests__/config.test.js +0 -102
- package/dist/azure/__tests__/config.test.js.map +0 -1
- package/dist/azure/__tests__/report.test.d.ts +0 -9
- package/dist/azure/__tests__/report.test.d.ts.map +0 -1
- package/dist/azure/__tests__/report.test.js +0 -191
- package/dist/azure/__tests__/report.test.js.map +0 -1
- package/dist/azure/__tests__/utils.test.d.ts +0 -15
- package/dist/azure/__tests__/utils.test.d.ts.map +0 -1
- package/dist/azure/__tests__/utils.test.js +0 -181
- package/dist/azure/__tests__/utils.test.js.map +0 -1
- package/dist/azure/analyzers/__tests__/advisor.test.d.ts +0 -9
- package/dist/azure/analyzers/__tests__/advisor.test.d.ts.map +0 -1
- package/dist/azure/analyzers/__tests__/advisor.test.js +0 -314
- package/dist/azure/analyzers/__tests__/advisor.test.js.map +0 -1
- package/dist/azure/pricing/__tests__/cache.test.d.ts +0 -8
- package/dist/azure/pricing/__tests__/cache.test.d.ts.map +0 -1
- package/dist/azure/pricing/__tests__/cache.test.js +0 -56
- package/dist/azure/pricing/__tests__/cache.test.js.map +0 -1
- package/dist/azure/pricing/__tests__/client.test.d.ts +0 -9
- package/dist/azure/pricing/__tests__/client.test.d.ts.map +0 -1
- package/dist/azure/pricing/__tests__/client.test.js +0 -181
- package/dist/azure/pricing/__tests__/client.test.js.map +0 -1
- package/dist/azure/pricing/__tests__/pricing-service.test.d.ts +0 -6
- package/dist/azure/pricing/__tests__/pricing-service.test.d.ts.map +0 -1
- package/dist/azure/pricing/__tests__/pricing-service.test.js +0 -283
- package/dist/azure/pricing/__tests__/pricing-service.test.js.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts +0 -5
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.js +0 -137
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.js.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts +0 -5
- package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/disk.test.js +0 -160
- package/dist/azure/pricing/resolvers/__tests__/disk.test.js.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts +0 -5
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js +0 -113
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts +0 -9
- package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts.map +0 -1
- package/dist/azure/pricing/resolvers/__tests__/vm.test.js +0 -204
- package/dist/azure/pricing/resolvers/__tests__/vm.test.js.map +0 -1
- package/dist/azure/resources/__tests__/app-service.test.d.ts +0 -5
- package/dist/azure/resources/__tests__/app-service.test.d.ts.map +0 -1
- package/dist/azure/resources/__tests__/app-service.test.js +0 -105
- package/dist/azure/resources/__tests__/app-service.test.js.map +0 -1
- package/dist/azure/resources/__tests__/storage.test.d.ts +0 -11
- package/dist/azure/resources/__tests__/storage.test.d.ts.map +0 -1
- package/dist/azure/resources/__tests__/storage.test.js +0 -99
- package/dist/azure/resources/__tests__/storage.test.js.map +0 -1
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the Public IP Retail Prices resolver.
|
|
3
|
-
*/
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { DiskCache } from "../../cache.js";
|
|
6
|
-
import { PricingClient } from "../../client.js";
|
|
7
|
-
import { makeTestCacheDir, removeTestCacheDir } from "../../test-cache-dir.js";
|
|
8
|
-
import { resolvePublicIpMonthlyPrice } from "../public-ip.js";
|
|
9
|
-
import { HOURS_PER_MONTH } from "../vm.js";
|
|
10
|
-
function makeFetch(body) {
|
|
11
|
-
return vi.fn().mockResolvedValue({
|
|
12
|
-
json: async () => body,
|
|
13
|
-
ok: true,
|
|
14
|
-
status: 200,
|
|
15
|
-
statusText: "OK",
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
let dir;
|
|
19
|
-
beforeEach(async () => {
|
|
20
|
-
dir = await makeTestCacheDir("public-ip-resolver");
|
|
21
|
-
});
|
|
22
|
-
afterEach(async () => {
|
|
23
|
-
await removeTestCacheDir(dir);
|
|
24
|
-
});
|
|
25
|
-
describe("resolvePublicIpMonthlyPrice", () => {
|
|
26
|
-
it("returns the monthly cost of a Standard static Public IP", async () => {
|
|
27
|
-
const body = {
|
|
28
|
-
Items: [
|
|
29
|
-
{
|
|
30
|
-
armRegionName: "westeurope",
|
|
31
|
-
currencyCode: "EUR",
|
|
32
|
-
meterName: "Standard Static IP Address",
|
|
33
|
-
productName: "Virtual Network",
|
|
34
|
-
retailPrice: 0.004,
|
|
35
|
-
skuName: "Standard",
|
|
36
|
-
type: "Consumption",
|
|
37
|
-
unitOfMeasure: "1 Hour",
|
|
38
|
-
unitPrice: 0.004,
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
NextPageLink: null,
|
|
42
|
-
};
|
|
43
|
-
const client = new PricingClient({
|
|
44
|
-
cache: new DiskCache({ dir }),
|
|
45
|
-
fetch: makeFetch(body),
|
|
46
|
-
});
|
|
47
|
-
const money = await resolvePublicIpMonthlyPrice(client, {
|
|
48
|
-
allocation: "static",
|
|
49
|
-
armRegionName: "westeurope",
|
|
50
|
-
sku: "standard",
|
|
51
|
-
});
|
|
52
|
-
expect(money).toEqual({
|
|
53
|
-
amount: Math.round(0.004 * HOURS_PER_MONTH * 100) / 100,
|
|
54
|
-
currency: "EUR",
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
it("filters by allocation token in meterName", async () => {
|
|
58
|
-
const body = {
|
|
59
|
-
Items: [
|
|
60
|
-
{
|
|
61
|
-
armRegionName: "westeurope",
|
|
62
|
-
currencyCode: "EUR",
|
|
63
|
-
meterName: "Basic Dynamic IP Address",
|
|
64
|
-
productName: "Virtual Network",
|
|
65
|
-
retailPrice: 0.0036,
|
|
66
|
-
skuName: "Basic",
|
|
67
|
-
type: "Consumption",
|
|
68
|
-
unitOfMeasure: "1 Hour",
|
|
69
|
-
unitPrice: 0.0036,
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
armRegionName: "westeurope",
|
|
73
|
-
currencyCode: "EUR",
|
|
74
|
-
meterName: "Basic Static IP Address",
|
|
75
|
-
productName: "Virtual Network",
|
|
76
|
-
retailPrice: 0.0036,
|
|
77
|
-
skuName: "Basic",
|
|
78
|
-
type: "Consumption",
|
|
79
|
-
unitOfMeasure: "1 Hour",
|
|
80
|
-
unitPrice: 0.0036,
|
|
81
|
-
},
|
|
82
|
-
],
|
|
83
|
-
NextPageLink: null,
|
|
84
|
-
};
|
|
85
|
-
const client = new PricingClient({
|
|
86
|
-
cache: new DiskCache({ dir }),
|
|
87
|
-
fetch: makeFetch(body),
|
|
88
|
-
});
|
|
89
|
-
const money = await resolvePublicIpMonthlyPrice(client, {
|
|
90
|
-
allocation: "dynamic",
|
|
91
|
-
armRegionName: "westeurope",
|
|
92
|
-
sku: "basic",
|
|
93
|
-
});
|
|
94
|
-
expect(money?.amount).toBeCloseTo(0.0036 * HOURS_PER_MONTH, 2);
|
|
95
|
-
});
|
|
96
|
-
it("returns undefined when no meter matches", async () => {
|
|
97
|
-
const body = {
|
|
98
|
-
Items: [],
|
|
99
|
-
NextPageLink: null,
|
|
100
|
-
};
|
|
101
|
-
const client = new PricingClient({
|
|
102
|
-
cache: new DiskCache({ dir }),
|
|
103
|
-
fetch: makeFetch(body),
|
|
104
|
-
});
|
|
105
|
-
const money = await resolvePublicIpMonthlyPrice(client, {
|
|
106
|
-
allocation: "static",
|
|
107
|
-
armRegionName: "westeurope",
|
|
108
|
-
sku: "standard",
|
|
109
|
-
});
|
|
110
|
-
expect(money).toBeUndefined();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
//# sourceMappingURL=public-ip.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"public-ip.test.js","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/public-ip.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the VM Retail Prices resolver.
|
|
3
|
-
*
|
|
4
|
-
* The Retail Prices API is mocked via the `FetchLike` indirection so the
|
|
5
|
-
* tests run hermetically. Fixtures live next to the tests to keep the
|
|
6
|
-
* surface explicit.
|
|
7
|
-
*/
|
|
8
|
-
export {};
|
|
9
|
-
//# sourceMappingURL=vm.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vm.test.d.ts","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/vm.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the VM Retail Prices resolver.
|
|
3
|
-
*
|
|
4
|
-
* The Retail Prices API is mocked via the `FetchLike` indirection so the
|
|
5
|
-
* tests run hermetically. Fixtures live next to the tests to keep the
|
|
6
|
-
* surface explicit.
|
|
7
|
-
*/
|
|
8
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
9
|
-
import { DiskCache } from "../../cache.js";
|
|
10
|
-
import { PricingClient } from "../../client.js";
|
|
11
|
-
import { makeTestCacheDir, removeTestCacheDir } from "../../test-cache-dir.js";
|
|
12
|
-
import { HOURS_PER_MONTH, normalizeArmRegion, resolveVmMonthlyPrice, } from "../vm.js";
|
|
13
|
-
function makeFetch(body) {
|
|
14
|
-
return vi.fn().mockResolvedValue({
|
|
15
|
-
json: async () => body,
|
|
16
|
-
ok: true,
|
|
17
|
-
status: 200,
|
|
18
|
-
statusText: "OK",
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
let dir;
|
|
22
|
-
beforeEach(async () => {
|
|
23
|
-
dir = await makeTestCacheDir("vm-resolver");
|
|
24
|
-
});
|
|
25
|
-
afterEach(async () => {
|
|
26
|
-
await removeTestCacheDir(dir);
|
|
27
|
-
});
|
|
28
|
-
describe("normalizeArmRegion", () => {
|
|
29
|
-
it.each([
|
|
30
|
-
["westeurope", "westeurope"],
|
|
31
|
-
["West Europe", "westeurope"],
|
|
32
|
-
["WEST EUROPE", "westeurope"],
|
|
33
|
-
[" West Europe ", "westeurope"],
|
|
34
|
-
["italynorth", "italynorth"],
|
|
35
|
-
])("normalises %j → %j", (input, expected) => {
|
|
36
|
-
expect(normalizeArmRegion(input)).toBe(expected);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe("resolveVmMonthlyPrice", () => {
|
|
40
|
-
it("picks the cheapest Linux consumption hourly item and returns 730×price", async () => {
|
|
41
|
-
const body = {
|
|
42
|
-
Items: [
|
|
43
|
-
{
|
|
44
|
-
armRegionName: "westeurope",
|
|
45
|
-
armSkuName: "Standard_B2s",
|
|
46
|
-
currencyCode: "EUR",
|
|
47
|
-
meterName: "B2s",
|
|
48
|
-
productName: "Virtual Machines BS Series",
|
|
49
|
-
retailPrice: 0.05,
|
|
50
|
-
skuName: "B2s",
|
|
51
|
-
type: "Consumption",
|
|
52
|
-
unitOfMeasure: "1 Hour",
|
|
53
|
-
unitPrice: 0.05,
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
armRegionName: "westeurope",
|
|
57
|
-
armSkuName: "Standard_B2s",
|
|
58
|
-
currencyCode: "EUR",
|
|
59
|
-
meterName: "B2s",
|
|
60
|
-
productName: "Virtual Machines BS Series Windows",
|
|
61
|
-
retailPrice: 0.09,
|
|
62
|
-
skuName: "B2s",
|
|
63
|
-
type: "Consumption",
|
|
64
|
-
unitOfMeasure: "1 Hour",
|
|
65
|
-
unitPrice: 0.09,
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
NextPageLink: null,
|
|
69
|
-
};
|
|
70
|
-
const client = new PricingClient({
|
|
71
|
-
cache: new DiskCache({ dir }),
|
|
72
|
-
fetch: makeFetch(body),
|
|
73
|
-
});
|
|
74
|
-
const money = await resolveVmMonthlyPrice(client, {
|
|
75
|
-
armRegionName: "westeurope",
|
|
76
|
-
armSkuName: "Standard_B2s",
|
|
77
|
-
});
|
|
78
|
-
expect(money).toEqual({
|
|
79
|
-
amount: Math.round(0.05 * HOURS_PER_MONTH * 100) / 100,
|
|
80
|
-
currency: "EUR",
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
it("picks the Windows item when os hint is 'windows'", async () => {
|
|
84
|
-
const body = {
|
|
85
|
-
Items: [
|
|
86
|
-
{
|
|
87
|
-
armRegionName: "westeurope",
|
|
88
|
-
armSkuName: "Standard_B2s",
|
|
89
|
-
currencyCode: "EUR",
|
|
90
|
-
productName: "Virtual Machines BS Series",
|
|
91
|
-
retailPrice: 0.05,
|
|
92
|
-
skuName: "B2s",
|
|
93
|
-
type: "Consumption",
|
|
94
|
-
unitOfMeasure: "1 Hour",
|
|
95
|
-
unitPrice: 0.05,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
armRegionName: "westeurope",
|
|
99
|
-
armSkuName: "Standard_B2s",
|
|
100
|
-
currencyCode: "EUR",
|
|
101
|
-
productName: "Virtual Machines BS Series Windows",
|
|
102
|
-
retailPrice: 0.09,
|
|
103
|
-
skuName: "B2s",
|
|
104
|
-
type: "Consumption",
|
|
105
|
-
unitOfMeasure: "1 Hour",
|
|
106
|
-
unitPrice: 0.09,
|
|
107
|
-
},
|
|
108
|
-
],
|
|
109
|
-
NextPageLink: null,
|
|
110
|
-
};
|
|
111
|
-
const client = new PricingClient({
|
|
112
|
-
cache: new DiskCache({ dir }),
|
|
113
|
-
fetch: makeFetch(body),
|
|
114
|
-
});
|
|
115
|
-
const money = await resolveVmMonthlyPrice(client, {
|
|
116
|
-
armRegionName: "westeurope",
|
|
117
|
-
armSkuName: "Standard_B2s",
|
|
118
|
-
os: "windows",
|
|
119
|
-
});
|
|
120
|
-
expect(money?.amount).toBeCloseTo(0.09 * HOURS_PER_MONTH, 2);
|
|
121
|
-
});
|
|
122
|
-
it("skips Spot / Low Priority items", async () => {
|
|
123
|
-
const body = {
|
|
124
|
-
Items: [
|
|
125
|
-
{
|
|
126
|
-
armRegionName: "westeurope",
|
|
127
|
-
armSkuName: "Standard_B2s",
|
|
128
|
-
currencyCode: "EUR",
|
|
129
|
-
productName: "Virtual Machines BS Series",
|
|
130
|
-
retailPrice: 0.01,
|
|
131
|
-
skuName: "B2s Spot",
|
|
132
|
-
type: "Consumption",
|
|
133
|
-
unitOfMeasure: "1 Hour",
|
|
134
|
-
unitPrice: 0.01,
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
armRegionName: "westeurope",
|
|
138
|
-
armSkuName: "Standard_B2s",
|
|
139
|
-
currencyCode: "EUR",
|
|
140
|
-
productName: "Virtual Machines BS Series",
|
|
141
|
-
retailPrice: 0.05,
|
|
142
|
-
skuName: "B2s",
|
|
143
|
-
type: "Consumption",
|
|
144
|
-
unitOfMeasure: "1 Hour",
|
|
145
|
-
unitPrice: 0.05,
|
|
146
|
-
},
|
|
147
|
-
],
|
|
148
|
-
NextPageLink: null,
|
|
149
|
-
};
|
|
150
|
-
const client = new PricingClient({
|
|
151
|
-
cache: new DiskCache({ dir }),
|
|
152
|
-
fetch: makeFetch(body),
|
|
153
|
-
});
|
|
154
|
-
const money = await resolveVmMonthlyPrice(client, {
|
|
155
|
-
armRegionName: "westeurope",
|
|
156
|
-
armSkuName: "Standard_B2s",
|
|
157
|
-
});
|
|
158
|
-
expect(money?.amount).toBeCloseTo(0.05 * HOURS_PER_MONTH, 2);
|
|
159
|
-
});
|
|
160
|
-
it("ignores Reservation entries", async () => {
|
|
161
|
-
const body = {
|
|
162
|
-
Items: [
|
|
163
|
-
{
|
|
164
|
-
armRegionName: "westeurope",
|
|
165
|
-
armSkuName: "Standard_B2s",
|
|
166
|
-
currencyCode: "EUR",
|
|
167
|
-
productName: "Virtual Machines BS Series",
|
|
168
|
-
reservationTerm: "1 Year",
|
|
169
|
-
retailPrice: 0.03,
|
|
170
|
-
skuName: "B2s",
|
|
171
|
-
type: "Reservation",
|
|
172
|
-
unitOfMeasure: "1 Hour",
|
|
173
|
-
unitPrice: 0.03,
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
NextPageLink: null,
|
|
177
|
-
};
|
|
178
|
-
const client = new PricingClient({
|
|
179
|
-
cache: new DiskCache({ dir }),
|
|
180
|
-
fetch: makeFetch(body),
|
|
181
|
-
});
|
|
182
|
-
const money = await resolveVmMonthlyPrice(client, {
|
|
183
|
-
armRegionName: "westeurope",
|
|
184
|
-
armSkuName: "Standard_B2s",
|
|
185
|
-
});
|
|
186
|
-
expect(money).toBeUndefined();
|
|
187
|
-
});
|
|
188
|
-
it("returns undefined when no items match", async () => {
|
|
189
|
-
const body = {
|
|
190
|
-
Items: [],
|
|
191
|
-
NextPageLink: null,
|
|
192
|
-
};
|
|
193
|
-
const client = new PricingClient({
|
|
194
|
-
cache: new DiskCache({ dir }),
|
|
195
|
-
fetch: makeFetch(body),
|
|
196
|
-
});
|
|
197
|
-
const money = await resolveVmMonthlyPrice(client, {
|
|
198
|
-
armRegionName: "westeurope",
|
|
199
|
-
armSkuName: "Standard_B2s",
|
|
200
|
-
});
|
|
201
|
-
expect(money).toBeUndefined();
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
//# sourceMappingURL=vm.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vm.test.js","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/vm.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-service.test.d.ts","sourceRoot":"","sources":["../../../../src/azure/resources/__tests__/app-service.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for analyzeAppServicePlan() pricing enrichment.
|
|
3
|
-
*/
|
|
4
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
-
import { DEFAULT_THRESHOLDS } from "../../../types.js";
|
|
6
|
-
vi.mock("../../utils.js", () => ({
|
|
7
|
-
getMetric: vi.fn(),
|
|
8
|
-
verboseLog: vi.fn(),
|
|
9
|
-
verboseLogAnalysisResult: vi.fn(),
|
|
10
|
-
verboseLogResourceStart: vi.fn(),
|
|
11
|
-
}));
|
|
12
|
-
import { getMetric } from "../../utils.js";
|
|
13
|
-
import { analyzeAppServicePlan } from "../app-service.js";
|
|
14
|
-
const RESOURCE = {
|
|
15
|
-
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Web/serverfarms/plan1",
|
|
16
|
-
location: "westeurope",
|
|
17
|
-
name: "plan1",
|
|
18
|
-
type: "Microsoft.Web/serverfarms",
|
|
19
|
-
};
|
|
20
|
-
const mockGetMetric = vi.mocked(getMetric);
|
|
21
|
-
function makeMonitorClient() {
|
|
22
|
-
return {
|
|
23
|
-
metrics: { list: vi.fn() },
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
function makePricingService() {
|
|
27
|
-
return {
|
|
28
|
-
resolveAppServicePlan: vi.fn().mockResolvedValue({
|
|
29
|
-
amount: 121.72,
|
|
30
|
-
currency: "EUR",
|
|
31
|
-
}),
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
function makeWebSiteClient(planDetails) {
|
|
35
|
-
const client = {
|
|
36
|
-
appServicePlans: {
|
|
37
|
-
get: vi.fn().mockResolvedValue({
|
|
38
|
-
location: "westeurope",
|
|
39
|
-
...planDetails,
|
|
40
|
-
}),
|
|
41
|
-
},
|
|
42
|
-
};
|
|
43
|
-
return client;
|
|
44
|
-
}
|
|
45
|
-
describe("analyzeAppServicePlan", () => {
|
|
46
|
-
beforeEach(() => {
|
|
47
|
-
mockGetMetric.mockReset();
|
|
48
|
-
mockGetMetric.mockResolvedValue(50);
|
|
49
|
-
});
|
|
50
|
-
it("enriches suspected unused plans with App Service Plan pricing", async () => {
|
|
51
|
-
const pricing = makePricingService();
|
|
52
|
-
const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
|
|
53
|
-
numberOfSites: 0,
|
|
54
|
-
reserved: true,
|
|
55
|
-
sku: {
|
|
56
|
-
capacity: 2,
|
|
57
|
-
name: "S1",
|
|
58
|
-
tier: "Standard",
|
|
59
|
-
},
|
|
60
|
-
}), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
|
|
61
|
-
expect(result.estimatedMonthlySavings).toEqual({
|
|
62
|
-
amount: 121.72,
|
|
63
|
-
currency: "EUR",
|
|
64
|
-
});
|
|
65
|
-
expect(pricing.resolveAppServicePlan).toHaveBeenCalledWith({
|
|
66
|
-
armRegionName: "westeurope",
|
|
67
|
-
os: "linux",
|
|
68
|
-
skuName: "S1",
|
|
69
|
-
workerCount: 2,
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
it("does not price Premium underutilization as full removable cost", async () => {
|
|
73
|
-
mockGetMetric.mockResolvedValue(1);
|
|
74
|
-
const pricing = makePricingService();
|
|
75
|
-
const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
|
|
76
|
-
numberOfSites: 2,
|
|
77
|
-
reserved: false,
|
|
78
|
-
sku: {
|
|
79
|
-
capacity: 3,
|
|
80
|
-
name: "P1v3",
|
|
81
|
-
tier: "PremiumV3",
|
|
82
|
-
},
|
|
83
|
-
}), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
|
|
84
|
-
expect(result.suspectedUnused).toBe(true);
|
|
85
|
-
expect(result.reason).toContain("Premium tier with low resource utilization");
|
|
86
|
-
expect(result.estimatedMonthlySavings).toBeUndefined();
|
|
87
|
-
expect(pricing.resolveAppServicePlan).not.toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
it("does not treat missing site count as an empty plan", async () => {
|
|
90
|
-
mockGetMetric.mockResolvedValue(50);
|
|
91
|
-
const pricing = makePricingService();
|
|
92
|
-
const result = await analyzeAppServicePlan(RESOURCE, makeWebSiteClient({
|
|
93
|
-
reserved: true,
|
|
94
|
-
sku: {
|
|
95
|
-
capacity: 2,
|
|
96
|
-
name: "S1",
|
|
97
|
-
tier: "Standard",
|
|
98
|
-
},
|
|
99
|
-
}), makeMonitorClient(), 30, DEFAULT_THRESHOLDS, false, undefined, pricing);
|
|
100
|
-
expect(result.suspectedUnused).toBe(false);
|
|
101
|
-
expect(result.estimatedMonthlySavings).toBeUndefined();
|
|
102
|
-
expect(pricing.resolveAppServicePlan).not.toHaveBeenCalled();
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
//# sourceMappingURL=app-service.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-service.test.js","sourceRoot":"","sources":["../../../../src/azure/resources/__tests__/app-service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for analyzeStorageAccount() — verifies that custom thresholds
|
|
3
|
-
* actually change the analysis outcome.
|
|
4
|
-
*
|
|
5
|
-
* Key scenario:
|
|
6
|
-
* - Metric: 30 avg transactions/day
|
|
7
|
-
* - Default threshold: 10 → 30 >= 10 → NOT flagged
|
|
8
|
-
* - Custom threshold: 50 → 30 < 50 → IS flagged ✓ proves the feature works
|
|
9
|
-
*/
|
|
10
|
-
export {};
|
|
11
|
-
//# sourceMappingURL=storage.test.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage.test.d.ts","sourceRoot":"","sources":["../../../../src/azure/resources/__tests__/storage.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for analyzeStorageAccount() — verifies that custom thresholds
|
|
3
|
-
* actually change the analysis outcome.
|
|
4
|
-
*
|
|
5
|
-
* Key scenario:
|
|
6
|
-
* - Metric: 30 avg transactions/day
|
|
7
|
-
* - Default threshold: 10 → 30 >= 10 → NOT flagged
|
|
8
|
-
* - Custom threshold: 50 → 30 < 50 → IS flagged ✓ proves the feature works
|
|
9
|
-
*/
|
|
10
|
-
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
11
|
-
import { DEFAULT_THRESHOLDS } from "../../../types.js";
|
|
12
|
-
// Mock the utils module so we control getMetric without real Azure calls
|
|
13
|
-
vi.mock("../../utils.js", () => ({
|
|
14
|
-
getMetric: vi.fn(),
|
|
15
|
-
verboseLog: vi.fn(),
|
|
16
|
-
verboseLogAnalysisResult: vi.fn(),
|
|
17
|
-
verboseLogResourceStart: vi.fn(),
|
|
18
|
-
}));
|
|
19
|
-
// Import after vi.mock so the module resolves the mock
|
|
20
|
-
import { getMetric } from "../../utils.js";
|
|
21
|
-
import { analyzeStorageAccount } from "../storage.js";
|
|
22
|
-
// ── helpers ────────────────────────────────────────────────────────────────
|
|
23
|
-
// Minimal stub: analyzeStorageAccount only needs monitorClient to be passed
|
|
24
|
-
// through to getMetric, which is fully mocked. We provide only the shape that
|
|
25
|
-
// TypeScript requires without unsafe type assertions.
|
|
26
|
-
const FAKE_CLIENT = {
|
|
27
|
-
metrics: { list: vi.fn() },
|
|
28
|
-
};
|
|
29
|
-
const FAKE_RESOURCE = {
|
|
30
|
-
id: "/subscriptions/sub1/resourceGroups/rg1/providers/Microsoft.Storage/storageAccounts/st1",
|
|
31
|
-
name: "st1",
|
|
32
|
-
type: "Microsoft.Storage/storageAccounts",
|
|
33
|
-
};
|
|
34
|
-
const mockGetMetric = vi.mocked(getMetric);
|
|
35
|
-
// ── tests ──────────────────────────────────────────────────────────────────
|
|
36
|
-
describe("analyzeStorageAccount — threshold sensitivity", () => {
|
|
37
|
-
beforeEach(() => {
|
|
38
|
-
mockGetMetric.mockReset();
|
|
39
|
-
});
|
|
40
|
-
describe("with DEFAULT threshold (transactionsPerDay = 10)", () => {
|
|
41
|
-
it("does NOT flag a resource with 30 transactions/day (30 ≥ 10)", async () => {
|
|
42
|
-
mockGetMetric.mockResolvedValue(30);
|
|
43
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, DEFAULT_THRESHOLDS);
|
|
44
|
-
expect(result.suspectedUnused).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
it("flags a resource with 5 transactions/day (5 < 10)", async () => {
|
|
47
|
-
mockGetMetric.mockResolvedValue(5);
|
|
48
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, DEFAULT_THRESHOLDS);
|
|
49
|
-
expect(result.suspectedUnused).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
it("does not flag when metric is exactly at the threshold (10)", async () => {
|
|
52
|
-
mockGetMetric.mockResolvedValue(10);
|
|
53
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, DEFAULT_THRESHOLDS);
|
|
54
|
-
// 10 < 10 is false → not flagged
|
|
55
|
-
expect(result.suspectedUnused).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
describe("with CUSTOM threshold (transactionsPerDay = 50)", () => {
|
|
59
|
-
const customThresholds = {
|
|
60
|
-
...DEFAULT_THRESHOLDS,
|
|
61
|
-
storage: { transactionsPerDay: 50 },
|
|
62
|
-
};
|
|
63
|
-
it("DOES flag a resource with 30 transactions/day (30 < 50) — proves override works", async () => {
|
|
64
|
-
mockGetMetric.mockResolvedValue(30);
|
|
65
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, customThresholds);
|
|
66
|
-
expect(result.suspectedUnused).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
it("does NOT flag a resource with 60 transactions/day (60 ≥ 50)", async () => {
|
|
69
|
-
mockGetMetric.mockResolvedValue(60);
|
|
70
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, customThresholds);
|
|
71
|
-
expect(result.suspectedUnused).toBe(false);
|
|
72
|
-
});
|
|
73
|
-
it("flags at threshold boundary (49 < 50)", async () => {
|
|
74
|
-
mockGetMetric.mockResolvedValue(49);
|
|
75
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, customThresholds);
|
|
76
|
-
expect(result.suspectedUnused).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe("edge cases", () => {
|
|
80
|
-
it("returns suspectedUnused: false when metric is null (data unavailable)", async () => {
|
|
81
|
-
mockGetMetric.mockResolvedValue(null);
|
|
82
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30, DEFAULT_THRESHOLDS);
|
|
83
|
-
expect(result.suspectedUnused).toBe(false);
|
|
84
|
-
});
|
|
85
|
-
it("returns suspectedUnused: false when resource id is missing", async () => {
|
|
86
|
-
const resourceWithoutId = { ...FAKE_RESOURCE, id: undefined };
|
|
87
|
-
const result = await analyzeStorageAccount(resourceWithoutId, FAKE_CLIENT, 30, DEFAULT_THRESHOLDS);
|
|
88
|
-
expect(result.suspectedUnused).toBe(false);
|
|
89
|
-
expect(mockGetMetric).not.toHaveBeenCalled();
|
|
90
|
-
});
|
|
91
|
-
it("applies default thresholds when the parameter is omitted", async () => {
|
|
92
|
-
// 5 < DEFAULT 10 → flagged even without explicit thresholds argument
|
|
93
|
-
mockGetMetric.mockResolvedValue(5);
|
|
94
|
-
const result = await analyzeStorageAccount(FAKE_RESOURCE, FAKE_CLIENT, 30);
|
|
95
|
-
expect(result.suspectedUnused).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
//# sourceMappingURL=storage.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"storage.test.js","sourceRoot":"","sources":["../../../../src/azure/resources/__tests__/storage.test.ts"],"names":[],"mappings":"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"}
|