@pagopa/dx-savemoney 0.3.3 → 0.4.0
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/README.md +12 -0
- package/dist/__tests__/finding.test.js +22 -0
- package/dist/__tests__/finding.test.js.map +1 -1
- package/dist/azure/__tests__/analyzer-tags.test.js +28 -1
- package/dist/azure/__tests__/analyzer-tags.test.js.map +1 -1
- package/dist/azure/__tests__/config.test.js +10 -0
- package/dist/azure/__tests__/config.test.js.map +1 -1
- package/dist/azure/__tests__/report.test.js +71 -0
- package/dist/azure/__tests__/report.test.js.map +1 -1
- package/dist/azure/analyzer.d.ts +8 -1
- package/dist/azure/analyzer.d.ts.map +1 -1
- package/dist/azure/analyzer.js +55 -37
- package/dist/azure/analyzer.js.map +1 -1
- package/dist/azure/analyzers/registry.d.ts.map +1 -1
- package/dist/azure/analyzers/registry.js +4 -4
- package/dist/azure/analyzers/registry.js.map +1 -1
- package/dist/azure/analyzers/types.d.ts +6 -0
- package/dist/azure/analyzers/types.d.ts.map +1 -1
- package/dist/azure/pricing/__tests__/cache.test.d.ts +8 -0
- package/dist/azure/pricing/__tests__/cache.test.d.ts.map +1 -0
- package/dist/azure/pricing/__tests__/cache.test.js +56 -0
- package/dist/azure/pricing/__tests__/cache.test.js.map +1 -0
- package/dist/azure/pricing/__tests__/client.test.d.ts +9 -0
- package/dist/azure/pricing/__tests__/client.test.d.ts.map +1 -0
- package/dist/azure/pricing/__tests__/client.test.js +181 -0
- package/dist/azure/pricing/__tests__/client.test.js.map +1 -0
- package/dist/azure/pricing/__tests__/pricing-service.test.d.ts +6 -0
- package/dist/azure/pricing/__tests__/pricing-service.test.d.ts.map +1 -0
- package/dist/azure/pricing/__tests__/pricing-service.test.js +283 -0
- package/dist/azure/pricing/__tests__/pricing-service.test.js.map +1 -0
- package/dist/azure/pricing/cache.d.ts +45 -0
- package/dist/azure/pricing/cache.d.ts.map +1 -0
- package/dist/azure/pricing/cache.js +79 -0
- package/dist/azure/pricing/cache.js.map +1 -0
- package/dist/azure/pricing/client.d.ts +67 -0
- package/dist/azure/pricing/client.d.ts.map +1 -0
- package/dist/azure/pricing/client.js +96 -0
- package/dist/azure/pricing/client.js.map +1 -0
- package/dist/azure/pricing/index.d.ts +6 -0
- package/dist/azure/pricing/index.d.ts.map +1 -0
- package/dist/azure/pricing/index.js +6 -0
- package/dist/azure/pricing/index.js.map +1 -0
- package/dist/azure/pricing/pricing-service.d.ts +46 -0
- package/dist/azure/pricing/pricing-service.d.ts.map +1 -0
- package/dist/azure/pricing/pricing-service.js +84 -0
- package/dist/azure/pricing/pricing-service.js.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts +5 -0
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.js +137 -0
- package/dist/azure/pricing/resolvers/__tests__/app-service.test.js.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts +5 -0
- package/dist/azure/pricing/resolvers/__tests__/disk.test.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/disk.test.js +160 -0
- package/dist/azure/pricing/resolvers/__tests__/disk.test.js.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts +5 -0
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js +113 -0
- package/dist/azure/pricing/resolvers/__tests__/public-ip.test.js.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts +9 -0
- package/dist/azure/pricing/resolvers/__tests__/vm.test.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/__tests__/vm.test.js +204 -0
- package/dist/azure/pricing/resolvers/__tests__/vm.test.js.map +1 -0
- package/dist/azure/pricing/resolvers/app-service.d.ts +27 -0
- package/dist/azure/pricing/resolvers/app-service.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/app-service.js +67 -0
- package/dist/azure/pricing/resolvers/app-service.js.map +1 -0
- package/dist/azure/pricing/resolvers/disk.d.ts +59 -0
- package/dist/azure/pricing/resolvers/disk.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/disk.js +140 -0
- package/dist/azure/pricing/resolvers/disk.js.map +1 -0
- package/dist/azure/pricing/resolvers/index.d.ts +12 -0
- package/dist/azure/pricing/resolvers/index.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/index.js +8 -0
- package/dist/azure/pricing/resolvers/index.js.map +1 -0
- package/dist/azure/pricing/resolvers/public-ip.d.ts +32 -0
- package/dist/azure/pricing/resolvers/public-ip.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/public-ip.js +56 -0
- package/dist/azure/pricing/resolvers/public-ip.js.map +1 -0
- package/dist/azure/pricing/resolvers/vm.d.ts +51 -0
- package/dist/azure/pricing/resolvers/vm.d.ts.map +1 -0
- package/dist/azure/pricing/resolvers/vm.js +89 -0
- package/dist/azure/pricing/resolvers/vm.js.map +1 -0
- package/dist/azure/pricing/schema.d.ts +88 -0
- package/dist/azure/pricing/schema.d.ts.map +1 -0
- package/dist/azure/pricing/schema.js +64 -0
- package/dist/azure/pricing/schema.js.map +1 -0
- package/dist/azure/pricing/test-cache-dir.d.ts +3 -0
- package/dist/azure/pricing/test-cache-dir.d.ts.map +1 -0
- package/dist/azure/pricing/test-cache-dir.js +17 -0
- package/dist/azure/pricing/test-cache-dir.js.map +1 -0
- package/dist/azure/report.d.ts.map +1 -1
- package/dist/azure/report.js +57 -18
- package/dist/azure/report.js.map +1 -1
- package/dist/azure/resources/__tests__/app-service.test.d.ts +5 -0
- package/dist/azure/resources/__tests__/app-service.test.d.ts.map +1 -0
- package/dist/azure/resources/__tests__/app-service.test.js +105 -0
- package/dist/azure/resources/__tests__/app-service.test.js.map +1 -0
- package/dist/azure/resources/app-service.d.ts +8 -3
- package/dist/azure/resources/app-service.d.ts.map +1 -1
- package/dist/azure/resources/app-service.js +35 -2
- package/dist/azure/resources/app-service.js.map +1 -1
- package/dist/azure/resources/disk.d.ts +3 -1
- package/dist/azure/resources/disk.d.ts.map +1 -1
- package/dist/azure/resources/disk.js +35 -3
- package/dist/azure/resources/disk.js.map +1 -1
- package/dist/azure/resources/public-ip.d.ts +3 -1
- package/dist/azure/resources/public-ip.d.ts.map +1 -1
- package/dist/azure/resources/public-ip.js +41 -3
- package/dist/azure/resources/public-ip.js.map +1 -1
- package/dist/azure/resources/vm.d.ts +4 -1
- package/dist/azure/resources/vm.d.ts.map +1 -1
- package/dist/azure/resources/vm.js +49 -6
- package/dist/azure/resources/vm.js.map +1 -1
- package/dist/azure/types.d.ts +13 -0
- package/dist/azure/types.d.ts.map +1 -1
- package/dist/finding.d.ts +3 -3
- package/dist/finding.d.ts.map +1 -1
- package/dist/finding.js +7 -0
- package/dist/finding.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +8 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/finding.test.ts +24 -0
- package/src/azure/__tests__/analyzer-tags.test.ts +36 -1
- package/src/azure/__tests__/config.test.ts +12 -0
- package/src/azure/__tests__/report.test.ts +78 -0
- package/src/azure/analyzer.ts +67 -43
- package/src/azure/analyzers/registry.ts +8 -2
- package/src/azure/analyzers/types.ts +6 -0
- package/src/azure/pricing/__tests__/cache.test.ts +67 -0
- package/src/azure/pricing/__tests__/client.test.ts +212 -0
- package/src/azure/pricing/__tests__/fixtures/vm-b2s-westeurope.json +51 -0
- package/src/azure/pricing/__tests__/pricing-service.test.ts +315 -0
- package/src/azure/pricing/cache.ts +92 -0
- package/src/azure/pricing/client.ts +137 -0
- package/src/azure/pricing/index.ts +6 -0
- package/src/azure/pricing/pricing-service.ts +125 -0
- package/src/azure/pricing/resolvers/__tests__/app-service.test.ts +154 -0
- package/src/azure/pricing/resolvers/__tests__/disk.test.ts +189 -0
- package/src/azure/pricing/resolvers/__tests__/public-ip.test.ts +130 -0
- package/src/azure/pricing/resolvers/__tests__/vm.test.ts +232 -0
- package/src/azure/pricing/resolvers/app-service.ts +102 -0
- package/src/azure/pricing/resolvers/disk.ts +170 -0
- package/src/azure/pricing/resolvers/index.ts +31 -0
- package/src/azure/pricing/resolvers/public-ip.ts +87 -0
- package/src/azure/pricing/resolvers/vm.ts +125 -0
- package/src/azure/pricing/schema.ts +69 -0
- package/src/azure/pricing/test-cache-dir.ts +26 -0
- package/src/azure/report.ts +86 -21
- package/src/azure/resources/__tests__/app-service.test.ts +165 -0
- package/src/azure/resources/app-service.ts +65 -4
- package/src/azure/resources/disk.ts +46 -4
- package/src/azure/resources/public-ip.ts +61 -2
- package/src/azure/resources/vm.ts +89 -11
- package/src/azure/types.ts +14 -0
- package/src/finding.ts +10 -3
- package/src/index.ts +3 -3
- package/src/types.ts +12 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Managed Disk 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 { pickTier, resolveDiskMonthlyPrice } from "../disk.js";
|
|
9
|
+
function makeFetch(body) {
|
|
10
|
+
const spy = vi.fn().mockResolvedValue({
|
|
11
|
+
json: async () => body,
|
|
12
|
+
ok: true,
|
|
13
|
+
status: 200,
|
|
14
|
+
statusText: "OK",
|
|
15
|
+
});
|
|
16
|
+
return { fetch: spy, spy };
|
|
17
|
+
}
|
|
18
|
+
let dir;
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
dir = await makeTestCacheDir("disk-resolver");
|
|
21
|
+
});
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
await removeTestCacheDir(dir);
|
|
24
|
+
});
|
|
25
|
+
describe("pickTier", () => {
|
|
26
|
+
it.each([
|
|
27
|
+
[1, 1],
|
|
28
|
+
[4, 1],
|
|
29
|
+
[5, 2],
|
|
30
|
+
[128, 10],
|
|
31
|
+
[129, 15],
|
|
32
|
+
[1024, 30],
|
|
33
|
+
[32768, 80],
|
|
34
|
+
])("maps %i GiB to tier %i", (sizeGiB, tier) => {
|
|
35
|
+
expect(pickTier(sizeGiB)).toBe(tier);
|
|
36
|
+
});
|
|
37
|
+
it("returns undefined for sizes above the largest tier", () => {
|
|
38
|
+
expect(pickTier(32769)).toBeUndefined();
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
describe("resolveDiskMonthlyPrice", () => {
|
|
42
|
+
it("resolves the monthly price for a Premium_LRS P10 disk", async () => {
|
|
43
|
+
const body = {
|
|
44
|
+
Items: [
|
|
45
|
+
{
|
|
46
|
+
armRegionName: "westeurope",
|
|
47
|
+
currencyCode: "EUR",
|
|
48
|
+
meterName: "P10 LRS Disk",
|
|
49
|
+
productName: "Premium SSD Managed Disks",
|
|
50
|
+
retailPrice: 19.71,
|
|
51
|
+
skuName: "P10 LRS",
|
|
52
|
+
type: "Consumption",
|
|
53
|
+
unitOfMeasure: "1/Month",
|
|
54
|
+
unitPrice: 19.71,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
NextPageLink: null,
|
|
58
|
+
};
|
|
59
|
+
const { fetch, spy } = makeFetch(body);
|
|
60
|
+
const client = new PricingClient({
|
|
61
|
+
cache: new DiskCache({ dir }),
|
|
62
|
+
fetch,
|
|
63
|
+
});
|
|
64
|
+
const money = await resolveDiskMonthlyPrice(client, {
|
|
65
|
+
armRegionName: "westeurope",
|
|
66
|
+
diskSizeGiB: 128,
|
|
67
|
+
sku: "Premium_LRS",
|
|
68
|
+
});
|
|
69
|
+
expect(money).toEqual({ amount: 19.71, currency: "EUR" });
|
|
70
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
71
|
+
// The OData filter must target the tier-derived skuName, not the raw
|
|
72
|
+
// managed-disk SKU. URLSearchParams encodes spaces as `+`.
|
|
73
|
+
const url = String(spy.mock.calls[0]?.[0]);
|
|
74
|
+
expect(url).toContain("skuName+eq+%27P10+LRS%27");
|
|
75
|
+
expect(url).toContain("productName+eq+%27Premium+SSD+Managed+Disks%27");
|
|
76
|
+
});
|
|
77
|
+
it("uses the ZRS product/replication for Premium_ZRS", async () => {
|
|
78
|
+
const body = {
|
|
79
|
+
Items: [
|
|
80
|
+
{
|
|
81
|
+
armRegionName: "westeurope",
|
|
82
|
+
currencyCode: "EUR",
|
|
83
|
+
meterName: "P10 ZRS Disk",
|
|
84
|
+
productName: "Premium SSD Managed Disks",
|
|
85
|
+
retailPrice: 24.64,
|
|
86
|
+
skuName: "P10 ZRS",
|
|
87
|
+
type: "Consumption",
|
|
88
|
+
unitOfMeasure: "1/Month",
|
|
89
|
+
unitPrice: 24.64,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
NextPageLink: null,
|
|
93
|
+
};
|
|
94
|
+
const { fetch, spy } = makeFetch(body);
|
|
95
|
+
const client = new PricingClient({
|
|
96
|
+
cache: new DiskCache({ dir }),
|
|
97
|
+
fetch,
|
|
98
|
+
});
|
|
99
|
+
const money = await resolveDiskMonthlyPrice(client, {
|
|
100
|
+
armRegionName: "westeurope",
|
|
101
|
+
diskSizeGiB: 128,
|
|
102
|
+
sku: "Premium_ZRS",
|
|
103
|
+
});
|
|
104
|
+
expect(money).toEqual({ amount: 24.64, currency: "EUR" });
|
|
105
|
+
const url = String(spy.mock.calls[0]?.[0]);
|
|
106
|
+
expect(url).toContain("skuName+eq+%27P10+ZRS%27");
|
|
107
|
+
});
|
|
108
|
+
it("returns undefined when the response has no matching entry", async () => {
|
|
109
|
+
const body = {
|
|
110
|
+
Items: [
|
|
111
|
+
{
|
|
112
|
+
armRegionName: "westeurope",
|
|
113
|
+
currencyCode: "EUR",
|
|
114
|
+
// Per-GB-month item; resolver must skip it because of unitOfMeasure.
|
|
115
|
+
productName: "Premium SSD Managed Disks",
|
|
116
|
+
retailPrice: 0.1,
|
|
117
|
+
skuName: "P10 LRS",
|
|
118
|
+
type: "Consumption",
|
|
119
|
+
unitOfMeasure: "1 GB/Month",
|
|
120
|
+
unitPrice: 0.1,
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
NextPageLink: null,
|
|
124
|
+
};
|
|
125
|
+
const client = new PricingClient({
|
|
126
|
+
cache: new DiskCache({ dir }),
|
|
127
|
+
fetch: makeFetch(body).fetch,
|
|
128
|
+
});
|
|
129
|
+
const money = await resolveDiskMonthlyPrice(client, {
|
|
130
|
+
armRegionName: "westeurope",
|
|
131
|
+
diskSizeGiB: 128,
|
|
132
|
+
sku: "Premium_LRS",
|
|
133
|
+
});
|
|
134
|
+
expect(money).toBeUndefined();
|
|
135
|
+
});
|
|
136
|
+
it("returns undefined when the disk size exceeds the largest tier", async () => {
|
|
137
|
+
const client = new PricingClient({
|
|
138
|
+
cache: new DiskCache({ dir }),
|
|
139
|
+
fetch: vi.fn(),
|
|
140
|
+
});
|
|
141
|
+
const money = await resolveDiskMonthlyPrice(client, {
|
|
142
|
+
armRegionName: "westeurope",
|
|
143
|
+
diskSizeGiB: 100_000,
|
|
144
|
+
sku: "Premium_LRS",
|
|
145
|
+
});
|
|
146
|
+
expect(money).toBeUndefined();
|
|
147
|
+
});
|
|
148
|
+
it("rejects invalid input via zod", async () => {
|
|
149
|
+
const client = new PricingClient({
|
|
150
|
+
cache: new DiskCache({ dir }),
|
|
151
|
+
fetch: vi.fn(),
|
|
152
|
+
});
|
|
153
|
+
await expect(resolveDiskMonthlyPrice(client, {
|
|
154
|
+
armRegionName: "westeurope",
|
|
155
|
+
diskSizeGiB: -1,
|
|
156
|
+
sku: "Premium_LRS",
|
|
157
|
+
})).rejects.toThrow();
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
//# sourceMappingURL=disk.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disk.test.js","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/disk.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"public-ip.test.d.ts","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/public-ip.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vm.test.d.ts","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/vm.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,204 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vm.test.js","sourceRoot":"","sources":["../../../../../src/azure/pricing/resolvers/__tests__/vm.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver: estimated monthly cost of an Azure App Service Plan.
|
|
3
|
+
*
|
|
4
|
+
* App Service Plan meters are published as hourly prices per plan instance.
|
|
5
|
+
* The resolver looks up all App Service meters in the target region, then
|
|
6
|
+
* matches the ARM plan SKU locally because Retail Prices uses inconsistent
|
|
7
|
+
* spacing for some families (for example `P1v3` vs `P1 v3`).
|
|
8
|
+
*/
|
|
9
|
+
import type { Money } from "../../../finding.js";
|
|
10
|
+
import type { PricingClient } from "../client.js";
|
|
11
|
+
export type AppServicePlanOs = "linux" | "windows";
|
|
12
|
+
export type ResolveAppServicePlanInput = {
|
|
13
|
+
/** ARM region name, e.g. `westeurope`. */
|
|
14
|
+
armRegionName: string;
|
|
15
|
+
/** Optional OS hint. When absent, the cheapest matching meter is used. */
|
|
16
|
+
os?: AppServicePlanOs;
|
|
17
|
+
/** App Service Plan SKU name, e.g. `S1`, `P1v3`, `P1mv3`. */
|
|
18
|
+
skuName: string;
|
|
19
|
+
/** Number of plan workers/instances to price. Defaults to 1. */
|
|
20
|
+
workerCount?: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Resolves the estimated monthly cost for an App Service Plan. Returns
|
|
24
|
+
* `undefined` if no matching consumption meter can be found.
|
|
25
|
+
*/
|
|
26
|
+
export declare function resolveAppServicePlanMonthlyPrice(client: PricingClient, input: ResolveAppServicePlanInput): Promise<Money | undefined>;
|
|
27
|
+
//# sourceMappingURL=app-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-service.d.ts","sourceRoot":"","sources":["../../../../src/azure/pricing/resolvers/app-service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAKlD,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,SAAS,CAAC;AAEnD,MAAM,MAAM,0BAA0B,GAAG;IACvC,0CAA0C;IAC1C,aAAa,EAAE,MAAM,CAAC;IACtB,0EAA0E;IAC1E,EAAE,CAAC,EAAE,gBAAgB,CAAC;IACtB,6DAA6D;IAC7D,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,iCAAiC,CACrD,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAoB5B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver: estimated monthly cost of an Azure App Service Plan.
|
|
3
|
+
*
|
|
4
|
+
* App Service Plan meters are published as hourly prices per plan instance.
|
|
5
|
+
* The resolver looks up all App Service meters in the target region, then
|
|
6
|
+
* matches the ARM plan SKU locally because Retail Prices uses inconsistent
|
|
7
|
+
* spacing for some families (for example `P1v3` vs `P1 v3`).
|
|
8
|
+
*/
|
|
9
|
+
import { HOURS_PER_MONTH, normalizeArmRegion } from "./vm.js";
|
|
10
|
+
/**
|
|
11
|
+
* Resolves the estimated monthly cost for an App Service Plan. Returns
|
|
12
|
+
* `undefined` if no matching consumption meter can be found.
|
|
13
|
+
*/
|
|
14
|
+
export async function resolveAppServicePlanMonthlyPrice(client, input) {
|
|
15
|
+
const filter = [
|
|
16
|
+
"serviceName eq 'Azure App Service'",
|
|
17
|
+
`armRegionName eq '${normalizeArmRegion(input.armRegionName)}'`,
|
|
18
|
+
].join(" and ");
|
|
19
|
+
const items = await client.query(filter);
|
|
20
|
+
const item = pickAppServicePlanItem(items, input);
|
|
21
|
+
if (!item) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
amount: round2(item.unitPrice *
|
|
26
|
+
HOURS_PER_MONTH *
|
|
27
|
+
normalizeWorkerCount(input.workerCount)),
|
|
28
|
+
currency: item.currencyCode,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function matchesOs(item, os) {
|
|
32
|
+
if (!os)
|
|
33
|
+
return true;
|
|
34
|
+
// Retail meters encode the OS variant in the product name, not in a
|
|
35
|
+
// dedicated field.
|
|
36
|
+
const product = item.productName?.toLowerCase() ?? "";
|
|
37
|
+
const isLinuxItem = product.includes("linux");
|
|
38
|
+
return os === "linux" ? isLinuxItem : !isLinuxItem;
|
|
39
|
+
}
|
|
40
|
+
function normalizeSkuName(value) {
|
|
41
|
+
// ARM uses compact SKU names (`P1v3`), while Retail Prices sometimes
|
|
42
|
+
// inserts spaces (`P1 v3`).
|
|
43
|
+
return value?.toLowerCase().replace(/\s+/g, "") ?? "";
|
|
44
|
+
}
|
|
45
|
+
function normalizeWorkerCount(workerCount) {
|
|
46
|
+
// App Service Plan billing is per whole worker instance; invalid or missing
|
|
47
|
+
// values fall back to the minimum billable instance.
|
|
48
|
+
if (!workerCount || workerCount < 1)
|
|
49
|
+
return 1;
|
|
50
|
+
return Math.ceil(workerCount);
|
|
51
|
+
}
|
|
52
|
+
function pickAppServicePlanItem(items, input) {
|
|
53
|
+
const skuName = normalizeSkuName(input.skuName);
|
|
54
|
+
const candidates = items.filter((it) => it.type === "Consumption" &&
|
|
55
|
+
it.unitOfMeasure === "1 Hour" &&
|
|
56
|
+
normalizeSkuName(it.skuName) === skuName &&
|
|
57
|
+
(it.productName?.toLowerCase().includes("app service") ?? false) &&
|
|
58
|
+
(it.productName?.toLowerCase().includes("plan") ?? false) &&
|
|
59
|
+
matchesOs(it, input.os));
|
|
60
|
+
if (candidates.length === 0)
|
|
61
|
+
return undefined;
|
|
62
|
+
return candidates.reduce((cheapest, it) => it.unitPrice < cheapest.unitPrice ? it : cheapest);
|
|
63
|
+
}
|
|
64
|
+
function round2(value) {
|
|
65
|
+
return Math.round(value * 100) / 100;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=app-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-service.js","sourceRoot":"","sources":["../../../../src/azure/pricing/resolvers/app-service.ts"],"names":[],"mappings":"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"}
|