@pagopa/dx-savemoney 0.3.2 → 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 +9 -9
- 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 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.d.ts","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/cache.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the disk-backed pricing cache.
|
|
3
|
+
*
|
|
4
|
+
* Each test uses a freshly-created temp directory so they do not leak
|
|
5
|
+
* state into the developer's real pricing cache.
|
|
6
|
+
*/
|
|
7
|
+
import { writeFile } from "node:fs/promises";
|
|
8
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
9
|
+
import { DiskCache } from "../cache.js";
|
|
10
|
+
import { makeTestCacheDir, removeTestCacheDir } from "../test-cache-dir.js";
|
|
11
|
+
describe("DiskCache", () => {
|
|
12
|
+
let dir;
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
dir = await makeTestCacheDir("cache");
|
|
15
|
+
});
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
await removeTestCacheDir(dir);
|
|
18
|
+
});
|
|
19
|
+
it("returns undefined on a miss", async () => {
|
|
20
|
+
const cache = new DiskCache({ dir });
|
|
21
|
+
await expect(cache.get("nope")).resolves.toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
it("returns the value written under the same key", async () => {
|
|
24
|
+
const cache = new DiskCache({ dir });
|
|
25
|
+
await cache.set("hello", { hits: 42 });
|
|
26
|
+
await expect(cache.get("hello")).resolves.toEqual({ hits: 42 });
|
|
27
|
+
});
|
|
28
|
+
it("treats different keys as independent entries", async () => {
|
|
29
|
+
const cache = new DiskCache({ dir });
|
|
30
|
+
await cache.set("a", 1);
|
|
31
|
+
await cache.set("b", 2);
|
|
32
|
+
await expect(cache.get("a")).resolves.toBe(1);
|
|
33
|
+
await expect(cache.get("b")).resolves.toBe(2);
|
|
34
|
+
});
|
|
35
|
+
it("returns undefined once the entry is older than ttlMs", async () => {
|
|
36
|
+
const cache = new DiskCache({ dir, ttlMs: 1 });
|
|
37
|
+
await cache.set("stale", { v: 1 });
|
|
38
|
+
// Wait long enough to outlast the 1 ms TTL.
|
|
39
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
40
|
+
await expect(cache.get("stale")).resolves.toBeUndefined();
|
|
41
|
+
});
|
|
42
|
+
it("returns undefined when the cache file contains invalid JSON", async () => {
|
|
43
|
+
const cache = new DiskCache({ dir });
|
|
44
|
+
const path = cache.pathFor("broken");
|
|
45
|
+
await writeFile(path, "{not json", "utf8");
|
|
46
|
+
await expect(cache.get("broken")).resolves.toBeUndefined();
|
|
47
|
+
});
|
|
48
|
+
it("derives different paths for different keys", () => {
|
|
49
|
+
const cache = new DiskCache({ dir });
|
|
50
|
+
expect(cache.pathFor("a")).not.toBe(cache.pathFor("b"));
|
|
51
|
+
});
|
|
52
|
+
it("refuses to remove directories outside the test cache parent", async () => {
|
|
53
|
+
await expect(removeTestCacheDir(process.cwd())).rejects.toThrow(/non-test cache directory/);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//# sourceMappingURL=cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.test.js","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/cache.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Azure Retail Prices HTTP client.
|
|
3
|
+
*
|
|
4
|
+
* The client is exercised against an in-process fake `fetch` so the suite
|
|
5
|
+
* never reaches the real `prices.azure.com` endpoint. A temp directory is
|
|
6
|
+
* used as cache so each test starts from a cold state.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=client.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.d.ts","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/client.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Azure Retail Prices HTTP client.
|
|
3
|
+
*
|
|
4
|
+
* The client is exercised against an in-process fake `fetch` so the suite
|
|
5
|
+
* never reaches the real `prices.azure.com` endpoint. A temp directory is
|
|
6
|
+
* used as cache so each test starts from a cold state.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile } from "node:fs/promises";
|
|
9
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
10
|
+
import { DiskCache } from "../cache.js";
|
|
11
|
+
import { PricingClient } from "../client.js";
|
|
12
|
+
import { makeTestCacheDir, removeTestCacheDir } from "../test-cache-dir.js";
|
|
13
|
+
const FIXTURE_PATH = new URL("./fixtures/vm-b2s-westeurope.json", import.meta.url);
|
|
14
|
+
async function loadFixture() {
|
|
15
|
+
const raw = await readFile(FIXTURE_PATH, "utf8");
|
|
16
|
+
return JSON.parse(raw);
|
|
17
|
+
}
|
|
18
|
+
function makeOkFetch(body) {
|
|
19
|
+
return vi.fn(async () => ({
|
|
20
|
+
json: async () => body,
|
|
21
|
+
ok: true,
|
|
22
|
+
status: 200,
|
|
23
|
+
statusText: "OK",
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
describe("PricingClient.query", () => {
|
|
27
|
+
let dir;
|
|
28
|
+
beforeEach(async () => {
|
|
29
|
+
dir = await makeTestCacheDir("client");
|
|
30
|
+
});
|
|
31
|
+
afterEach(async () => {
|
|
32
|
+
await removeTestCacheDir(dir);
|
|
33
|
+
});
|
|
34
|
+
it("returns the items from a single page response", async () => {
|
|
35
|
+
const body = await loadFixture();
|
|
36
|
+
const fetchImpl = makeOkFetch(body);
|
|
37
|
+
const client = new PricingClient({
|
|
38
|
+
cache: new DiskCache({ dir }),
|
|
39
|
+
fetch: fetchImpl,
|
|
40
|
+
});
|
|
41
|
+
const items = await client.query("serviceName eq 'Virtual Machines'");
|
|
42
|
+
expect(items).toHaveLength(2);
|
|
43
|
+
expect(items[0].armSkuName).toBe("Standard_B2s");
|
|
44
|
+
expect(items[0].unitPrice).toBe(0.057);
|
|
45
|
+
expect(fetchImpl).toHaveBeenCalledTimes(1);
|
|
46
|
+
});
|
|
47
|
+
it("includes the filter, currency, and api-version in the request URL", async () => {
|
|
48
|
+
const body = await loadFixture();
|
|
49
|
+
const fetchImpl = makeOkFetch(body);
|
|
50
|
+
const client = new PricingClient({
|
|
51
|
+
cache: new DiskCache({ dir }),
|
|
52
|
+
currencyCode: "EUR",
|
|
53
|
+
fetch: fetchImpl,
|
|
54
|
+
});
|
|
55
|
+
await client.query("serviceName eq 'Virtual Machines'");
|
|
56
|
+
const url = fetchImpl.mock
|
|
57
|
+
.calls[0][0];
|
|
58
|
+
expect(url).toContain("prices.azure.com/api/retail/prices");
|
|
59
|
+
expect(url).toContain("currencyCode=EUR");
|
|
60
|
+
expect(url).toContain("api-version=");
|
|
61
|
+
// URLSearchParams encodes spaces as `+` (form-urlencoded), so we
|
|
62
|
+
// convert them back before decoding the filter expression.
|
|
63
|
+
const decoded = decodeURIComponent(url.replace(/\+/g, " "));
|
|
64
|
+
expect(decoded).toContain("$filter=serviceName eq 'Virtual Machines'");
|
|
65
|
+
});
|
|
66
|
+
it("follows NextPageLink across multiple pages", async () => {
|
|
67
|
+
const page1 = {
|
|
68
|
+
Count: 1,
|
|
69
|
+
Items: [
|
|
70
|
+
{
|
|
71
|
+
currencyCode: "EUR",
|
|
72
|
+
retailPrice: 1,
|
|
73
|
+
type: "Consumption",
|
|
74
|
+
unitOfMeasure: "1 Hour",
|
|
75
|
+
unitPrice: 1,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
NextPageLink: "https://prices.azure.com/api/retail/prices?next=2",
|
|
79
|
+
};
|
|
80
|
+
const page2 = {
|
|
81
|
+
Count: 1,
|
|
82
|
+
Items: [
|
|
83
|
+
{
|
|
84
|
+
currencyCode: "EUR",
|
|
85
|
+
retailPrice: 2,
|
|
86
|
+
type: "Consumption",
|
|
87
|
+
unitOfMeasure: "1 Hour",
|
|
88
|
+
unitPrice: 2,
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
NextPageLink: null,
|
|
92
|
+
};
|
|
93
|
+
const fetchImpl = vi
|
|
94
|
+
.fn()
|
|
95
|
+
.mockResolvedValueOnce({
|
|
96
|
+
json: async () => page1,
|
|
97
|
+
ok: true,
|
|
98
|
+
status: 200,
|
|
99
|
+
statusText: "OK",
|
|
100
|
+
})
|
|
101
|
+
.mockResolvedValueOnce({
|
|
102
|
+
json: async () => page2,
|
|
103
|
+
ok: true,
|
|
104
|
+
status: 200,
|
|
105
|
+
statusText: "OK",
|
|
106
|
+
});
|
|
107
|
+
const client = new PricingClient({
|
|
108
|
+
cache: new DiskCache({ dir }),
|
|
109
|
+
fetch: fetchImpl,
|
|
110
|
+
});
|
|
111
|
+
const items = await client.query("anything");
|
|
112
|
+
expect(items.map((i) => i.unitPrice)).toEqual([1, 2]);
|
|
113
|
+
expect(fetchImpl).toHaveBeenCalledTimes(2);
|
|
114
|
+
});
|
|
115
|
+
it("serves a cached response without hitting the network on the second call", async () => {
|
|
116
|
+
const body = await loadFixture();
|
|
117
|
+
const fetchImpl = makeOkFetch(body);
|
|
118
|
+
const cache = new DiskCache({ dir });
|
|
119
|
+
const client = new PricingClient({ cache, fetch: fetchImpl });
|
|
120
|
+
const first = await client.query("filter-a");
|
|
121
|
+
const second = await client.query("filter-a");
|
|
122
|
+
expect(first).toEqual(second);
|
|
123
|
+
expect(fetchImpl).toHaveBeenCalledTimes(1);
|
|
124
|
+
});
|
|
125
|
+
it("keeps cache entries scoped by currency", async () => {
|
|
126
|
+
const body = await loadFixture();
|
|
127
|
+
const fetchImpl = makeOkFetch(body);
|
|
128
|
+
const cache = new DiskCache({ dir });
|
|
129
|
+
const eur = new PricingClient({
|
|
130
|
+
cache,
|
|
131
|
+
currencyCode: "EUR",
|
|
132
|
+
fetch: fetchImpl,
|
|
133
|
+
});
|
|
134
|
+
const usd = new PricingClient({
|
|
135
|
+
cache,
|
|
136
|
+
currencyCode: "USD",
|
|
137
|
+
fetch: fetchImpl,
|
|
138
|
+
});
|
|
139
|
+
await eur.query("same-filter");
|
|
140
|
+
await usd.query("same-filter");
|
|
141
|
+
// Two distinct cache keys → two API round-trips, no cross-contamination.
|
|
142
|
+
expect(fetchImpl).toHaveBeenCalledTimes(2);
|
|
143
|
+
});
|
|
144
|
+
it("throws a descriptive error on a non-2xx response", async () => {
|
|
145
|
+
const fetchImpl = vi.fn(async () => ({
|
|
146
|
+
json: async () => ({}),
|
|
147
|
+
ok: false,
|
|
148
|
+
status: 503,
|
|
149
|
+
statusText: "Service Unavailable",
|
|
150
|
+
}));
|
|
151
|
+
const client = new PricingClient({
|
|
152
|
+
cache: new DiskCache({ dir }),
|
|
153
|
+
fetch: fetchImpl,
|
|
154
|
+
});
|
|
155
|
+
await expect(client.query("x")).rejects.toThrow(/503/);
|
|
156
|
+
});
|
|
157
|
+
it("throws when the response body fails schema validation", async () => {
|
|
158
|
+
const fetchImpl = makeOkFetch({ wrong: "shape" });
|
|
159
|
+
const client = new PricingClient({
|
|
160
|
+
cache: new DiskCache({ dir }),
|
|
161
|
+
fetch: fetchImpl,
|
|
162
|
+
});
|
|
163
|
+
await expect(client.query("x")).rejects.toThrow();
|
|
164
|
+
});
|
|
165
|
+
it("throws a descriptive error when the response body is not JSON", async () => {
|
|
166
|
+
const fetchImpl = vi.fn(async () => ({
|
|
167
|
+
json: async () => {
|
|
168
|
+
throw new SyntaxError("Unexpected token");
|
|
169
|
+
},
|
|
170
|
+
ok: true,
|
|
171
|
+
status: 200,
|
|
172
|
+
statusText: "OK",
|
|
173
|
+
}));
|
|
174
|
+
const client = new PricingClient({
|
|
175
|
+
cache: new DiskCache({ dir }),
|
|
176
|
+
fetch: fetchImpl,
|
|
177
|
+
});
|
|
178
|
+
await expect(client.query("x")).rejects.toThrow(/invalid JSON/);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.test.js","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing-service.test.d.ts","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/pricing-service.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the PricingService façade — covers memoization and error
|
|
3
|
+
* swallowing. The underlying resolvers are exercised in their own tests.
|
|
4
|
+
*/
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
import { DiskCache } from "../cache.js";
|
|
7
|
+
import { PricingClient } from "../client.js";
|
|
8
|
+
import { PricingService } from "../pricing-service.js";
|
|
9
|
+
import { makeTestCacheDir, removeTestCacheDir } from "../test-cache-dir.js";
|
|
10
|
+
function makeFetch(body) {
|
|
11
|
+
const spy = vi.fn().mockResolvedValue({
|
|
12
|
+
json: async () => body,
|
|
13
|
+
ok: true,
|
|
14
|
+
status: 200,
|
|
15
|
+
statusText: "OK",
|
|
16
|
+
});
|
|
17
|
+
return { fetch: spy, spy };
|
|
18
|
+
}
|
|
19
|
+
let dir;
|
|
20
|
+
beforeEach(async () => {
|
|
21
|
+
dir = await makeTestCacheDir("pricing-service");
|
|
22
|
+
});
|
|
23
|
+
afterEach(async () => {
|
|
24
|
+
await removeTestCacheDir(dir);
|
|
25
|
+
});
|
|
26
|
+
describe("PricingService", () => {
|
|
27
|
+
it("memoizes identical VM lookups within the same instance", async () => {
|
|
28
|
+
const body = {
|
|
29
|
+
Items: [
|
|
30
|
+
{
|
|
31
|
+
armRegionName: "westeurope",
|
|
32
|
+
armSkuName: "Standard_B2s",
|
|
33
|
+
currencyCode: "EUR",
|
|
34
|
+
productName: "Virtual Machines BS Series",
|
|
35
|
+
retailPrice: 0.05,
|
|
36
|
+
skuName: "B2s",
|
|
37
|
+
type: "Consumption",
|
|
38
|
+
unitOfMeasure: "1 Hour",
|
|
39
|
+
unitPrice: 0.05,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
NextPageLink: null,
|
|
43
|
+
};
|
|
44
|
+
const { fetch, spy } = makeFetch(body);
|
|
45
|
+
const client = new PricingClient({
|
|
46
|
+
cache: new DiskCache({ dir }),
|
|
47
|
+
fetch,
|
|
48
|
+
});
|
|
49
|
+
const service = new PricingService(client);
|
|
50
|
+
const [a, b] = await Promise.all([
|
|
51
|
+
service.resolveVm({
|
|
52
|
+
armRegionName: "westeurope",
|
|
53
|
+
armSkuName: "Standard_B2s",
|
|
54
|
+
}),
|
|
55
|
+
service.resolveVm({
|
|
56
|
+
armRegionName: "westeurope",
|
|
57
|
+
armSkuName: "Standard_B2s",
|
|
58
|
+
}),
|
|
59
|
+
]);
|
|
60
|
+
expect(a).toEqual(b);
|
|
61
|
+
// Only one underlying fetch despite two concurrent calls.
|
|
62
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
63
|
+
});
|
|
64
|
+
it("returns undefined and does not throw when the resolver errors", async () => {
|
|
65
|
+
const client = new PricingClient({
|
|
66
|
+
cache: new DiskCache({ dir }),
|
|
67
|
+
fetch: vi.fn().mockRejectedValue(new Error("boom")),
|
|
68
|
+
});
|
|
69
|
+
const service = new PricingService(client);
|
|
70
|
+
const result = await service.resolveVm({
|
|
71
|
+
armRegionName: "westeurope",
|
|
72
|
+
armSkuName: "Standard_B2s",
|
|
73
|
+
});
|
|
74
|
+
expect(result).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
it("scopes the in-memory memo by os hint", async () => {
|
|
77
|
+
const body = {
|
|
78
|
+
Items: [
|
|
79
|
+
{
|
|
80
|
+
armRegionName: "westeurope",
|
|
81
|
+
armSkuName: "Standard_B2s",
|
|
82
|
+
currencyCode: "EUR",
|
|
83
|
+
productName: "Virtual Machines BS Series",
|
|
84
|
+
retailPrice: 0.05,
|
|
85
|
+
skuName: "B2s",
|
|
86
|
+
type: "Consumption",
|
|
87
|
+
unitOfMeasure: "1 Hour",
|
|
88
|
+
unitPrice: 0.05,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
armRegionName: "westeurope",
|
|
92
|
+
armSkuName: "Standard_B2s",
|
|
93
|
+
currencyCode: "EUR",
|
|
94
|
+
productName: "Virtual Machines BS Series Windows",
|
|
95
|
+
retailPrice: 0.09,
|
|
96
|
+
skuName: "B2s",
|
|
97
|
+
type: "Consumption",
|
|
98
|
+
unitOfMeasure: "1 Hour",
|
|
99
|
+
unitPrice: 0.09,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
NextPageLink: null,
|
|
103
|
+
};
|
|
104
|
+
const { fetch, spy } = makeFetch(body);
|
|
105
|
+
const client = new PricingClient({
|
|
106
|
+
cache: new DiskCache({ dir }),
|
|
107
|
+
fetch,
|
|
108
|
+
});
|
|
109
|
+
const service = new PricingService(client);
|
|
110
|
+
const linux = await service.resolveVm({
|
|
111
|
+
armRegionName: "westeurope",
|
|
112
|
+
armSkuName: "Standard_B2s",
|
|
113
|
+
os: "linux",
|
|
114
|
+
});
|
|
115
|
+
const windows = await service.resolveVm({
|
|
116
|
+
armRegionName: "westeurope",
|
|
117
|
+
armSkuName: "Standard_B2s",
|
|
118
|
+
os: "windows",
|
|
119
|
+
});
|
|
120
|
+
expect(linux?.amount).toBeLessThan(windows?.amount ?? 0);
|
|
121
|
+
// Same underlying filter, served from DiskCache — one fetch only.
|
|
122
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
describe("PricingService disk memoization", () => {
|
|
126
|
+
it("memoizes identical disk lookups within the same instance", async () => {
|
|
127
|
+
const body = {
|
|
128
|
+
Items: [
|
|
129
|
+
{
|
|
130
|
+
armRegionName: "westeurope",
|
|
131
|
+
currencyCode: "EUR",
|
|
132
|
+
productName: "Premium SSD Managed Disks",
|
|
133
|
+
retailPrice: 19.71,
|
|
134
|
+
skuName: "P10 LRS",
|
|
135
|
+
type: "Consumption",
|
|
136
|
+
unitOfMeasure: "1/Month",
|
|
137
|
+
unitPrice: 19.71,
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
NextPageLink: null,
|
|
141
|
+
};
|
|
142
|
+
const { fetch, spy } = makeFetch(body);
|
|
143
|
+
const client = new PricingClient({
|
|
144
|
+
cache: new DiskCache({ dir }),
|
|
145
|
+
fetch,
|
|
146
|
+
});
|
|
147
|
+
const service = new PricingService(client);
|
|
148
|
+
const [a, b] = await Promise.all([
|
|
149
|
+
service.resolveDisk({
|
|
150
|
+
armRegionName: "westeurope",
|
|
151
|
+
diskSizeGiB: 128,
|
|
152
|
+
sku: "Premium_LRS",
|
|
153
|
+
}),
|
|
154
|
+
service.resolveDisk({
|
|
155
|
+
armRegionName: "westeurope",
|
|
156
|
+
diskSizeGiB: 128,
|
|
157
|
+
sku: "Premium_LRS",
|
|
158
|
+
}),
|
|
159
|
+
]);
|
|
160
|
+
expect(a).toEqual(b);
|
|
161
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
162
|
+
});
|
|
163
|
+
it("memoizes disk lookups with display and ARM region names as the same key", async () => {
|
|
164
|
+
const body = {
|
|
165
|
+
Items: [
|
|
166
|
+
{
|
|
167
|
+
armRegionName: "westeurope",
|
|
168
|
+
currencyCode: "EUR",
|
|
169
|
+
productName: "Premium SSD Managed Disks",
|
|
170
|
+
retailPrice: 19.71,
|
|
171
|
+
skuName: "P10 LRS",
|
|
172
|
+
type: "Consumption",
|
|
173
|
+
unitOfMeasure: "1/Month",
|
|
174
|
+
unitPrice: 19.71,
|
|
175
|
+
},
|
|
176
|
+
],
|
|
177
|
+
NextPageLink: null,
|
|
178
|
+
};
|
|
179
|
+
const { fetch, spy } = makeFetch(body);
|
|
180
|
+
const client = new PricingClient({
|
|
181
|
+
cache: new DiskCache({ dir }),
|
|
182
|
+
fetch,
|
|
183
|
+
});
|
|
184
|
+
const service = new PricingService(client);
|
|
185
|
+
const [a, b] = await Promise.all([
|
|
186
|
+
service.resolveDisk({
|
|
187
|
+
armRegionName: "West Europe",
|
|
188
|
+
diskSizeGiB: 128,
|
|
189
|
+
sku: "Premium_LRS",
|
|
190
|
+
}),
|
|
191
|
+
service.resolveDisk({
|
|
192
|
+
armRegionName: "westeurope",
|
|
193
|
+
diskSizeGiB: 128,
|
|
194
|
+
sku: "Premium_LRS",
|
|
195
|
+
}),
|
|
196
|
+
]);
|
|
197
|
+
expect(a).toEqual(b);
|
|
198
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
describe("PricingService App Service Plan memoization", () => {
|
|
202
|
+
it("memoizes identical App Service Plan lookups within the same instance", async () => {
|
|
203
|
+
const body = {
|
|
204
|
+
Items: [
|
|
205
|
+
{
|
|
206
|
+
armRegionName: "westeurope",
|
|
207
|
+
currencyCode: "EUR",
|
|
208
|
+
meterName: "S1 App",
|
|
209
|
+
productName: "Azure App Service Standard Plan - Linux",
|
|
210
|
+
retailPrice: 0.08337,
|
|
211
|
+
skuName: "S1",
|
|
212
|
+
type: "Consumption",
|
|
213
|
+
unitOfMeasure: "1 Hour",
|
|
214
|
+
unitPrice: 0.08337,
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
NextPageLink: null,
|
|
218
|
+
};
|
|
219
|
+
const { fetch, spy } = makeFetch(body);
|
|
220
|
+
const client = new PricingClient({
|
|
221
|
+
cache: new DiskCache({ dir }),
|
|
222
|
+
fetch,
|
|
223
|
+
});
|
|
224
|
+
const service = new PricingService(client);
|
|
225
|
+
const [a, b] = await Promise.all([
|
|
226
|
+
service.resolveAppServicePlan({
|
|
227
|
+
armRegionName: "westeurope",
|
|
228
|
+
os: "linux",
|
|
229
|
+
skuName: "S1",
|
|
230
|
+
workerCount: 1,
|
|
231
|
+
}),
|
|
232
|
+
service.resolveAppServicePlan({
|
|
233
|
+
armRegionName: "westeurope",
|
|
234
|
+
os: "linux",
|
|
235
|
+
skuName: "S1",
|
|
236
|
+
workerCount: 1,
|
|
237
|
+
}),
|
|
238
|
+
]);
|
|
239
|
+
expect(a).toEqual(b);
|
|
240
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
241
|
+
});
|
|
242
|
+
it("memoizes App Service Plan lookups by canonical region and worker count", async () => {
|
|
243
|
+
const body = {
|
|
244
|
+
Items: [
|
|
245
|
+
{
|
|
246
|
+
armRegionName: "westeurope",
|
|
247
|
+
currencyCode: "EUR",
|
|
248
|
+
meterName: "S1 App",
|
|
249
|
+
productName: "Azure App Service Standard Plan - Linux",
|
|
250
|
+
retailPrice: 0.08337,
|
|
251
|
+
skuName: "S1",
|
|
252
|
+
type: "Consumption",
|
|
253
|
+
unitOfMeasure: "1 Hour",
|
|
254
|
+
unitPrice: 0.08337,
|
|
255
|
+
},
|
|
256
|
+
],
|
|
257
|
+
NextPageLink: null,
|
|
258
|
+
};
|
|
259
|
+
const { fetch, spy } = makeFetch(body);
|
|
260
|
+
const client = new PricingClient({
|
|
261
|
+
cache: new DiskCache({ dir }),
|
|
262
|
+
fetch,
|
|
263
|
+
});
|
|
264
|
+
const service = new PricingService(client);
|
|
265
|
+
const [a, b] = await Promise.all([
|
|
266
|
+
service.resolveAppServicePlan({
|
|
267
|
+
armRegionName: "West Europe",
|
|
268
|
+
os: "linux",
|
|
269
|
+
skuName: "S1",
|
|
270
|
+
workerCount: 1.2,
|
|
271
|
+
}),
|
|
272
|
+
service.resolveAppServicePlan({
|
|
273
|
+
armRegionName: "westeurope",
|
|
274
|
+
os: "linux",
|
|
275
|
+
skuName: "S1",
|
|
276
|
+
workerCount: 2,
|
|
277
|
+
}),
|
|
278
|
+
]);
|
|
279
|
+
expect(a).toEqual(b);
|
|
280
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
//# sourceMappingURL=pricing-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing-service.test.js","sourceRoot":"","sources":["../../../../src/azure/pricing/__tests__/pricing-service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Disk-backed cache for Azure Retail Prices API responses.
|
|
3
|
+
*
|
|
4
|
+
* Retail prices change rarely (Azure publishes a new snapshot at most once
|
|
5
|
+
* a month) so a long TTL is safe and dramatically cuts cold-start latency
|
|
6
|
+
* for `dx savemoney`. Each cache entry is a JSON file whose name is the
|
|
7
|
+
* SHA-256 of the request key (the OData `$filter` string); the entry is
|
|
8
|
+
* considered fresh while `now - mtime <= ttl`.
|
|
9
|
+
*
|
|
10
|
+
* The cache directory is created lazily on first write. Read/write
|
|
11
|
+
* failures never throw: a corrupted or unreadable entry simply behaves
|
|
12
|
+
* like a miss so the caller refetches from the API. This matches the
|
|
13
|
+
* "best-effort cache" semantics expected by HTTP-level caches.
|
|
14
|
+
*/
|
|
15
|
+
/** Defaults documented in the package README. */
|
|
16
|
+
export declare const DEFAULT_CACHE_DIR: string;
|
|
17
|
+
export declare const DEFAULT_CACHE_TTL_MS: number;
|
|
18
|
+
export type DiskCacheOptions = {
|
|
19
|
+
/** Absolute path where cache entries are stored. */
|
|
20
|
+
dir?: string;
|
|
21
|
+
/** Maximum age of an entry before it is treated as a miss. */
|
|
22
|
+
ttlMs?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Best-effort JSON cache. Keys are arbitrary strings; values are anything
|
|
26
|
+
* serialisable via `JSON.stringify`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class DiskCache {
|
|
29
|
+
readonly dir: string;
|
|
30
|
+
readonly ttlMs: number;
|
|
31
|
+
constructor(options?: DiskCacheOptions);
|
|
32
|
+
/**
|
|
33
|
+
* Returns the cached value for `key` if present and still fresh.
|
|
34
|
+
* Returns `undefined` on miss, expiry, or any I/O / parse error.
|
|
35
|
+
*/
|
|
36
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
37
|
+
/** Absolute filesystem path used to store `key`. Exposed for tests. */
|
|
38
|
+
pathFor(key: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Persists `value` under `key`. Silently swallows write errors so a
|
|
41
|
+
* read-only filesystem cannot break the calling pipeline.
|
|
42
|
+
*/
|
|
43
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../src/azure/pricing/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,QAI7B,CAAC;AACF,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;GAGG;AACH,qBAAa,SAAS;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,OAAO,GAAE,gBAAqB;IAK1C;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAiBjD,uEAAuE;IACvE,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAK5B;;;OAGG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAYnD"}
|