@reventlessdev/reventless-seed-aws 1.0.0-alpha.4 → 1.0.0-alpha.6
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/CHANGELOG.md +15 -0
- package/lib/bs/.compiler.log +2 -2
- package/lib/bs/.sourcedirs.json +1 -0
- package/lib/bs/compiler-info.json +2 -2
- package/lib/bs/src/ReventlessSeedAws.ast +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmi +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmj +0 -0
- package/lib/bs/src/ReventlessSeedAws.cmt +0 -0
- package/lib/bs/src/ReventlessSeedAws.res +131 -75
- package/lib/bs/src/ReventlessSeedAws.res.mjs +63 -30
- package/lib/bs/src/ReventlessSeedAws_Reset.ast +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.cmi +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.cmt +0 -0
- package/lib/bs/src/ReventlessSeedAws_Reset.res +8 -15
- package/lib/bs/tests/EndpointResolutionTest.ast +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmi +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmj +0 -0
- package/lib/bs/tests/EndpointResolutionTest.cmt +0 -0
- package/lib/bs/tests/EndpointResolutionTest.res +180 -0
- package/lib/bs/tests/EndpointResolutionTest.res.mjs +208 -0
- package/lib/ocaml/.compiler.log +2 -2
- package/lib/ocaml/EndpointResolutionTest.ast +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmi +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmj +0 -0
- package/lib/ocaml/EndpointResolutionTest.cmt +0 -0
- package/lib/ocaml/EndpointResolutionTest.res +180 -0
- package/lib/ocaml/ReventlessSeedAws.ast +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmi +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmj +0 -0
- package/lib/ocaml/ReventlessSeedAws.cmt +0 -0
- package/lib/ocaml/ReventlessSeedAws.res +131 -75
- package/lib/ocaml/ReventlessSeedAws_Reset.ast +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.cmi +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.cmt +0 -0
- package/lib/ocaml/ReventlessSeedAws_Reset.res +8 -15
- package/package.json +6 -4
- package/rescript.json +8 -1
- package/src/ReventlessSeedAws.res +131 -75
- package/src/ReventlessSeedAws.res.mjs +63 -30
- package/src/ReventlessSeedAws_Reset.res +8 -15
- package/tests/EndpointResolutionTest.res +180 -0
- package/tests/EndpointResolutionTest.res.mjs +208 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Which document `resolveEndpoints` reads, and what each arm takes from it.
|
|
2
|
+
//
|
|
3
|
+
// `endpointsFrom` is the pure half of `resolveEndpoints`; the impure half is a
|
|
4
|
+
// `pulumi stack output` subprocess and a `config.json` fetch, and the decision
|
|
5
|
+
// these tests pin is in neither. The defect they lock out: **neither arm read
|
|
6
|
+
// `uploadEndpoints`**, so a platform that publishes a presign endpoint per
|
|
7
|
+
// declared store resolved "" and the seeder reported it as serving no uploads.
|
|
8
|
+
// The stack-outputs arm is the one that matters — a platform deploying no host
|
|
9
|
+
// shell publishes no `hostShellUrl` by construction, so it takes that arm, and
|
|
10
|
+
// the per-store endpoint is its only way to reach the store.
|
|
11
|
+
|
|
12
|
+
open JestGlobals
|
|
13
|
+
open ReventlessSeed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// Every value read here has an env override, and an ambient one in the runner's
|
|
17
|
+
// environment (AWS_REGION is routinely set) would silently stand in for the
|
|
18
|
+
// document under test. An empty string reads as unset.
|
|
19
|
+
let clearOverrides = () =>
|
|
20
|
+
[
|
|
21
|
+
"REVENTLESS_GRAPHQL_ENDPOINT",
|
|
22
|
+
"REVENTLESS_UPLOAD_ENDPOINT",
|
|
23
|
+
"AWS_REGION",
|
|
24
|
+
"COGNITO_CLIENT_ID",
|
|
25
|
+
"SEED_SKIP_UPLOADS",
|
|
26
|
+
]->Array.forEach(k => NodeProcess.env->Dict.set(k, ""))
|
|
27
|
+
|
|
28
|
+
let obj = entries => JSON.Encode.object(entries->Dict.fromArray)
|
|
29
|
+
let str = JSON.Encode.string
|
|
30
|
+
|
|
31
|
+
// A host shell's config.json and the stack outputs of a platform that serves no
|
|
32
|
+
// shell — the two documents the two arms read, minus the upload keys each test
|
|
33
|
+
// supplies.
|
|
34
|
+
let config = (~extra: array<(string, JSON.t)>) =>
|
|
35
|
+
obj(
|
|
36
|
+
Array.concat(
|
|
37
|
+
[
|
|
38
|
+
("apiEndpoint", str("https://api.example/graphql")),
|
|
39
|
+
("region", str("eu-west-1")),
|
|
40
|
+
("cognitoClientId", str("client-abc")),
|
|
41
|
+
],
|
|
42
|
+
extra,
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
let outputs = (~extra: array<(string, JSON.t)>) =>
|
|
47
|
+
obj(
|
|
48
|
+
Array.concat(
|
|
49
|
+
[
|
|
50
|
+
("domainApiEndpoint", str("https://domain.example/graphql")),
|
|
51
|
+
("cognitoRegion", str("eu-west-1")),
|
|
52
|
+
("cognitoUserPoolClientId", str("client-abc")),
|
|
53
|
+
],
|
|
54
|
+
extra,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
let storeEndpoints = obj([("Catalog.productImages", str("https://presign.example/"))])
|
|
59
|
+
|
|
60
|
+
describe("endpointsFrom — host shell arm", () => {
|
|
61
|
+
beforeEach(clearOverrides)
|
|
62
|
+
|
|
63
|
+
testSync("reads the legacy single service and the per-store map together", () => {
|
|
64
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
65
|
+
~stack="alpha",
|
|
66
|
+
HostShellConfig(
|
|
67
|
+
config(
|
|
68
|
+
~extra=[
|
|
69
|
+
("uploadEndpoint", str("https://legacy.example/")),
|
|
70
|
+
("uploadEndpoints", storeEndpoints),
|
|
71
|
+
],
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
expect(eps.uploadEndpoint)->toBe("https://legacy.example/")
|
|
76
|
+
expect(eps.uploadEndpoints)->toEqual(
|
|
77
|
+
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// A shell fronting only declared stores writes no singular key. That used to
|
|
82
|
+
// fail the whole run before a command was sent ("deployment is missing
|
|
83
|
+
// uploadEndpoint"), which is a harder failure than the one this plan fixes.
|
|
84
|
+
testSync("a shell publishing only per-store endpoints resolves an empty legacy service", () => {
|
|
85
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
86
|
+
~stack="alpha",
|
|
87
|
+
HostShellConfig(config(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
88
|
+
)
|
|
89
|
+
expect(eps.uploadEndpoint)->toBe("")
|
|
90
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
testSync("REVENTLESS_UPLOAD_ENDPOINT overrides the legacy service and leaves the map alone", () => {
|
|
94
|
+
NodeProcess.env->Dict.set("REVENTLESS_UPLOAD_ENDPOINT", "https://override.example/")
|
|
95
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
96
|
+
~stack="alpha",
|
|
97
|
+
HostShellConfig(
|
|
98
|
+
config(
|
|
99
|
+
~extra=[
|
|
100
|
+
("uploadEndpoint", str("https://legacy.example/")),
|
|
101
|
+
("uploadEndpoints", storeEndpoints),
|
|
102
|
+
],
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
expect(eps.uploadEndpoint)->toBe("https://override.example/")
|
|
107
|
+
expect(eps.uploadEndpoints->Dict.get("Catalog.productImages"))->toEqual(
|
|
108
|
+
Some("https://presign.example/"),
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe("endpointsFrom — stack outputs arm", () => {
|
|
114
|
+
beforeEach(clearOverrides)
|
|
115
|
+
|
|
116
|
+
// The lookup that was missing entirely: this arm had no upload source but the
|
|
117
|
+
// env var, so a `PlatformOwned` deployment resolved "" whatever it published.
|
|
118
|
+
testSync("reads the uploadEndpoints stack output", () => {
|
|
119
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
120
|
+
~stack="pr-verify",
|
|
121
|
+
StackOutputs(outputs(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
122
|
+
)
|
|
123
|
+
expect(eps.uploadEndpoints)->toEqual(
|
|
124
|
+
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
125
|
+
)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Declaring no store is a legitimate deployment, not a broken one — it stays a
|
|
129
|
+
// no-op at the data set rather than failing the run.
|
|
130
|
+
testSync("a deployment declaring no store resolves an empty map without failing", () => {
|
|
131
|
+
let eps = ReventlessSeedAws.endpointsFrom(~stack="pr-verify", StackOutputs(outputs(~extra=[])))
|
|
132
|
+
expect(eps.uploadEndpoint)->toBe("")
|
|
133
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual([])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
testSync("prefers the merged API endpoint over the per-plugin one", () => {
|
|
137
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
138
|
+
~stack="pr-verify",
|
|
139
|
+
StackOutputs(outputs(~extra=[("domainMergedApiEndpoint", str("https://merged.example/"))])),
|
|
140
|
+
)
|
|
141
|
+
expect(eps.graphql)->toBe("https://merged.example/")
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
testSync("fails naming both keys when the stack exports neither API endpoint", () =>
|
|
145
|
+
switch ReventlessSeedAws.endpointsFrom(
|
|
146
|
+
~stack="pr-verify",
|
|
147
|
+
StackOutputs(obj([("cognitoRegion", str("eu-west-1"))])),
|
|
148
|
+
) {
|
|
149
|
+
| _ => fail("expected endpointsFrom to fail")
|
|
150
|
+
| exception Seed.Failed(msg) =>
|
|
151
|
+
expect(msg)->toContain("domainMergedApiEndpoint")
|
|
152
|
+
expect(msg)->toContain("domainApiEndpoint")
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe("endpointsFrom — malformed uploadEndpoints", () => {
|
|
158
|
+
beforeEach(clearOverrides)
|
|
159
|
+
|
|
160
|
+
// One bad member should not cost the run every endpoint it could have used.
|
|
161
|
+
testSync("drops a non-string member rather than the whole map", () => {
|
|
162
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
163
|
+
~stack="pr-verify",
|
|
164
|
+
StackOutputs(
|
|
165
|
+
outputs(
|
|
166
|
+
~extra=[
|
|
167
|
+
(
|
|
168
|
+
"uploadEndpoints",
|
|
169
|
+
obj([
|
|
170
|
+
("Catalog.productImages", str("https://presign.example/")),
|
|
171
|
+
("Catalog.broken", JSON.Encode.int(7)),
|
|
172
|
+
]),
|
|
173
|
+
),
|
|
174
|
+
],
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
179
|
+
})
|
|
180
|
+
})
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as JestGlobals from "@reventlessdev/rescript-jest/src/JestGlobals.res.mjs";
|
|
4
|
+
import * as ReventlessSeedAws from "../src/ReventlessSeedAws.res.mjs";
|
|
5
|
+
import * as Seed$ReventlessSeed from "@reventlessdev/reventless-seed/src/Seed.res.mjs";
|
|
6
|
+
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
7
|
+
|
|
8
|
+
function clearOverrides() {
|
|
9
|
+
[
|
|
10
|
+
"REVENTLESS_GRAPHQL_ENDPOINT",
|
|
11
|
+
"REVENTLESS_UPLOAD_ENDPOINT",
|
|
12
|
+
"AWS_REGION",
|
|
13
|
+
"COGNITO_CLIENT_ID",
|
|
14
|
+
"SEED_SKIP_UPLOADS"
|
|
15
|
+
].forEach(k => {
|
|
16
|
+
process.env[k] = "";
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function obj(entries) {
|
|
21
|
+
return Object.fromEntries(entries);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function str(prim) {
|
|
25
|
+
return prim;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function config(extra) {
|
|
29
|
+
return Object.fromEntries([
|
|
30
|
+
[
|
|
31
|
+
"apiEndpoint",
|
|
32
|
+
"https://api.example/graphql"
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
"region",
|
|
36
|
+
"eu-west-1"
|
|
37
|
+
],
|
|
38
|
+
[
|
|
39
|
+
"cognitoClientId",
|
|
40
|
+
"client-abc"
|
|
41
|
+
]
|
|
42
|
+
].concat(extra));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function outputs(extra) {
|
|
46
|
+
return Object.fromEntries([
|
|
47
|
+
[
|
|
48
|
+
"domainApiEndpoint",
|
|
49
|
+
"https://domain.example/graphql"
|
|
50
|
+
],
|
|
51
|
+
[
|
|
52
|
+
"cognitoRegion",
|
|
53
|
+
"eu-west-1"
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
"cognitoUserPoolClientId",
|
|
57
|
+
"client-abc"
|
|
58
|
+
]
|
|
59
|
+
].concat(extra));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let storeEndpoints = Object.fromEntries([[
|
|
63
|
+
"Catalog.productImages",
|
|
64
|
+
"https://presign.example/"
|
|
65
|
+
]]);
|
|
66
|
+
|
|
67
|
+
globalThis.describe("endpointsFrom — host shell arm", () => {
|
|
68
|
+
globalThis.beforeEach(clearOverrides);
|
|
69
|
+
globalThis.test("reads the legacy single service and the per-store map together", () => {
|
|
70
|
+
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
71
|
+
TAG: "HostShellConfig",
|
|
72
|
+
_0: config([
|
|
73
|
+
[
|
|
74
|
+
"uploadEndpoint",
|
|
75
|
+
"https://legacy.example/"
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
"uploadEndpoints",
|
|
79
|
+
storeEndpoints
|
|
80
|
+
]
|
|
81
|
+
])
|
|
82
|
+
});
|
|
83
|
+
globalThis.expect(eps.uploadEndpoint).toBe("https://legacy.example/");
|
|
84
|
+
globalThis.expect(eps.uploadEndpoints).toEqual(Object.fromEntries([[
|
|
85
|
+
"Catalog.productImages",
|
|
86
|
+
"https://presign.example/"
|
|
87
|
+
]]));
|
|
88
|
+
});
|
|
89
|
+
globalThis.test("a shell publishing only per-store endpoints resolves an empty legacy service", () => {
|
|
90
|
+
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
91
|
+
TAG: "HostShellConfig",
|
|
92
|
+
_0: config([[
|
|
93
|
+
"uploadEndpoints",
|
|
94
|
+
storeEndpoints
|
|
95
|
+
]])
|
|
96
|
+
});
|
|
97
|
+
globalThis.expect(eps.uploadEndpoint).toBe("");
|
|
98
|
+
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual(["Catalog.productImages"]);
|
|
99
|
+
});
|
|
100
|
+
globalThis.test("REVENTLESS_UPLOAD_ENDPOINT overrides the legacy service and leaves the map alone", () => {
|
|
101
|
+
process.env["REVENTLESS_UPLOAD_ENDPOINT"] = "https://override.example/";
|
|
102
|
+
let eps = ReventlessSeedAws.endpointsFrom("alpha", {
|
|
103
|
+
TAG: "HostShellConfig",
|
|
104
|
+
_0: config([
|
|
105
|
+
[
|
|
106
|
+
"uploadEndpoint",
|
|
107
|
+
"https://legacy.example/"
|
|
108
|
+
],
|
|
109
|
+
[
|
|
110
|
+
"uploadEndpoints",
|
|
111
|
+
storeEndpoints
|
|
112
|
+
]
|
|
113
|
+
])
|
|
114
|
+
});
|
|
115
|
+
globalThis.expect(eps.uploadEndpoint).toBe("https://override.example/");
|
|
116
|
+
globalThis.expect(eps.uploadEndpoints["Catalog.productImages"]).toEqual("https://presign.example/");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
globalThis.describe("endpointsFrom — stack outputs arm", () => {
|
|
121
|
+
globalThis.beforeEach(clearOverrides);
|
|
122
|
+
globalThis.test("reads the uploadEndpoints stack output", () => {
|
|
123
|
+
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
124
|
+
TAG: "StackOutputs",
|
|
125
|
+
_0: outputs([[
|
|
126
|
+
"uploadEndpoints",
|
|
127
|
+
storeEndpoints
|
|
128
|
+
]])
|
|
129
|
+
});
|
|
130
|
+
globalThis.expect(eps.uploadEndpoints).toEqual(Object.fromEntries([[
|
|
131
|
+
"Catalog.productImages",
|
|
132
|
+
"https://presign.example/"
|
|
133
|
+
]]));
|
|
134
|
+
});
|
|
135
|
+
globalThis.test("a deployment declaring no store resolves an empty map without failing", () => {
|
|
136
|
+
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
137
|
+
TAG: "StackOutputs",
|
|
138
|
+
_0: outputs([])
|
|
139
|
+
});
|
|
140
|
+
globalThis.expect(eps.uploadEndpoint).toBe("");
|
|
141
|
+
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual([]);
|
|
142
|
+
});
|
|
143
|
+
globalThis.test("prefers the merged API endpoint over the per-plugin one", () => {
|
|
144
|
+
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
145
|
+
TAG: "StackOutputs",
|
|
146
|
+
_0: outputs([[
|
|
147
|
+
"domainMergedApiEndpoint",
|
|
148
|
+
"https://merged.example/"
|
|
149
|
+
]])
|
|
150
|
+
});
|
|
151
|
+
globalThis.expect(eps.graphql).toBe("https://merged.example/");
|
|
152
|
+
});
|
|
153
|
+
globalThis.test("fails naming both keys when the stack exports neither API endpoint", () => {
|
|
154
|
+
let val;
|
|
155
|
+
try {
|
|
156
|
+
val = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
157
|
+
TAG: "StackOutputs",
|
|
158
|
+
_0: Object.fromEntries([[
|
|
159
|
+
"cognitoRegion",
|
|
160
|
+
"eu-west-1"
|
|
161
|
+
]])
|
|
162
|
+
});
|
|
163
|
+
} catch (raw_msg) {
|
|
164
|
+
let msg = Primitive_exceptions.internalToException(raw_msg);
|
|
165
|
+
if (msg.RE_EXN_ID === Seed$ReventlessSeed.Failed) {
|
|
166
|
+
let msg$1 = msg._1;
|
|
167
|
+
globalThis.expect(msg$1).toContain("domainMergedApiEndpoint");
|
|
168
|
+
globalThis.expect(msg$1).toContain("domainApiEndpoint");
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
throw msg;
|
|
172
|
+
}
|
|
173
|
+
JestGlobals.fail("expected endpointsFrom to fail");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
globalThis.describe("endpointsFrom — malformed uploadEndpoints", () => {
|
|
178
|
+
globalThis.beforeEach(clearOverrides);
|
|
179
|
+
globalThis.test("drops a non-string member rather than the whole map", () => {
|
|
180
|
+
let eps = ReventlessSeedAws.endpointsFrom("pr-verify", {
|
|
181
|
+
TAG: "StackOutputs",
|
|
182
|
+
_0: outputs([[
|
|
183
|
+
"uploadEndpoints",
|
|
184
|
+
Object.fromEntries([
|
|
185
|
+
[
|
|
186
|
+
"Catalog.productImages",
|
|
187
|
+
"https://presign.example/"
|
|
188
|
+
],
|
|
189
|
+
[
|
|
190
|
+
"Catalog.broken",
|
|
191
|
+
7
|
|
192
|
+
]
|
|
193
|
+
])
|
|
194
|
+
]])
|
|
195
|
+
});
|
|
196
|
+
globalThis.expect(Object.keys(eps.uploadEndpoints)).toEqual(["Catalog.productImages"]);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
export {
|
|
201
|
+
clearOverrides,
|
|
202
|
+
obj,
|
|
203
|
+
str,
|
|
204
|
+
config,
|
|
205
|
+
outputs,
|
|
206
|
+
storeEndpoints,
|
|
207
|
+
}
|
|
208
|
+
/* storeEndpoints Not a pure module */
|
package/lib/ocaml/.compiler.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
#Start(
|
|
2
|
-
#Done(
|
|
1
|
+
#Start(1785535392961)
|
|
2
|
+
#Done(1785535392990)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Which document `resolveEndpoints` reads, and what each arm takes from it.
|
|
2
|
+
//
|
|
3
|
+
// `endpointsFrom` is the pure half of `resolveEndpoints`; the impure half is a
|
|
4
|
+
// `pulumi stack output` subprocess and a `config.json` fetch, and the decision
|
|
5
|
+
// these tests pin is in neither. The defect they lock out: **neither arm read
|
|
6
|
+
// `uploadEndpoints`**, so a platform that publishes a presign endpoint per
|
|
7
|
+
// declared store resolved "" and the seeder reported it as serving no uploads.
|
|
8
|
+
// The stack-outputs arm is the one that matters — a platform deploying no host
|
|
9
|
+
// shell publishes no `hostShellUrl` by construction, so it takes that arm, and
|
|
10
|
+
// the per-store endpoint is its only way to reach the store.
|
|
11
|
+
|
|
12
|
+
open JestGlobals
|
|
13
|
+
open ReventlessSeed
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
// Every value read here has an env override, and an ambient one in the runner's
|
|
17
|
+
// environment (AWS_REGION is routinely set) would silently stand in for the
|
|
18
|
+
// document under test. An empty string reads as unset.
|
|
19
|
+
let clearOverrides = () =>
|
|
20
|
+
[
|
|
21
|
+
"REVENTLESS_GRAPHQL_ENDPOINT",
|
|
22
|
+
"REVENTLESS_UPLOAD_ENDPOINT",
|
|
23
|
+
"AWS_REGION",
|
|
24
|
+
"COGNITO_CLIENT_ID",
|
|
25
|
+
"SEED_SKIP_UPLOADS",
|
|
26
|
+
]->Array.forEach(k => NodeProcess.env->Dict.set(k, ""))
|
|
27
|
+
|
|
28
|
+
let obj = entries => JSON.Encode.object(entries->Dict.fromArray)
|
|
29
|
+
let str = JSON.Encode.string
|
|
30
|
+
|
|
31
|
+
// A host shell's config.json and the stack outputs of a platform that serves no
|
|
32
|
+
// shell — the two documents the two arms read, minus the upload keys each test
|
|
33
|
+
// supplies.
|
|
34
|
+
let config = (~extra: array<(string, JSON.t)>) =>
|
|
35
|
+
obj(
|
|
36
|
+
Array.concat(
|
|
37
|
+
[
|
|
38
|
+
("apiEndpoint", str("https://api.example/graphql")),
|
|
39
|
+
("region", str("eu-west-1")),
|
|
40
|
+
("cognitoClientId", str("client-abc")),
|
|
41
|
+
],
|
|
42
|
+
extra,
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
let outputs = (~extra: array<(string, JSON.t)>) =>
|
|
47
|
+
obj(
|
|
48
|
+
Array.concat(
|
|
49
|
+
[
|
|
50
|
+
("domainApiEndpoint", str("https://domain.example/graphql")),
|
|
51
|
+
("cognitoRegion", str("eu-west-1")),
|
|
52
|
+
("cognitoUserPoolClientId", str("client-abc")),
|
|
53
|
+
],
|
|
54
|
+
extra,
|
|
55
|
+
),
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
let storeEndpoints = obj([("Catalog.productImages", str("https://presign.example/"))])
|
|
59
|
+
|
|
60
|
+
describe("endpointsFrom — host shell arm", () => {
|
|
61
|
+
beforeEach(clearOverrides)
|
|
62
|
+
|
|
63
|
+
testSync("reads the legacy single service and the per-store map together", () => {
|
|
64
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
65
|
+
~stack="alpha",
|
|
66
|
+
HostShellConfig(
|
|
67
|
+
config(
|
|
68
|
+
~extra=[
|
|
69
|
+
("uploadEndpoint", str("https://legacy.example/")),
|
|
70
|
+
("uploadEndpoints", storeEndpoints),
|
|
71
|
+
],
|
|
72
|
+
),
|
|
73
|
+
),
|
|
74
|
+
)
|
|
75
|
+
expect(eps.uploadEndpoint)->toBe("https://legacy.example/")
|
|
76
|
+
expect(eps.uploadEndpoints)->toEqual(
|
|
77
|
+
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// A shell fronting only declared stores writes no singular key. That used to
|
|
82
|
+
// fail the whole run before a command was sent ("deployment is missing
|
|
83
|
+
// uploadEndpoint"), which is a harder failure than the one this plan fixes.
|
|
84
|
+
testSync("a shell publishing only per-store endpoints resolves an empty legacy service", () => {
|
|
85
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
86
|
+
~stack="alpha",
|
|
87
|
+
HostShellConfig(config(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
88
|
+
)
|
|
89
|
+
expect(eps.uploadEndpoint)->toBe("")
|
|
90
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
testSync("REVENTLESS_UPLOAD_ENDPOINT overrides the legacy service and leaves the map alone", () => {
|
|
94
|
+
NodeProcess.env->Dict.set("REVENTLESS_UPLOAD_ENDPOINT", "https://override.example/")
|
|
95
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
96
|
+
~stack="alpha",
|
|
97
|
+
HostShellConfig(
|
|
98
|
+
config(
|
|
99
|
+
~extra=[
|
|
100
|
+
("uploadEndpoint", str("https://legacy.example/")),
|
|
101
|
+
("uploadEndpoints", storeEndpoints),
|
|
102
|
+
],
|
|
103
|
+
),
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
expect(eps.uploadEndpoint)->toBe("https://override.example/")
|
|
107
|
+
expect(eps.uploadEndpoints->Dict.get("Catalog.productImages"))->toEqual(
|
|
108
|
+
Some("https://presign.example/"),
|
|
109
|
+
)
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe("endpointsFrom — stack outputs arm", () => {
|
|
114
|
+
beforeEach(clearOverrides)
|
|
115
|
+
|
|
116
|
+
// The lookup that was missing entirely: this arm had no upload source but the
|
|
117
|
+
// env var, so a `PlatformOwned` deployment resolved "" whatever it published.
|
|
118
|
+
testSync("reads the uploadEndpoints stack output", () => {
|
|
119
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
120
|
+
~stack="pr-verify",
|
|
121
|
+
StackOutputs(outputs(~extra=[("uploadEndpoints", storeEndpoints)])),
|
|
122
|
+
)
|
|
123
|
+
expect(eps.uploadEndpoints)->toEqual(
|
|
124
|
+
Dict.fromArray([("Catalog.productImages", "https://presign.example/")]),
|
|
125
|
+
)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
// Declaring no store is a legitimate deployment, not a broken one — it stays a
|
|
129
|
+
// no-op at the data set rather than failing the run.
|
|
130
|
+
testSync("a deployment declaring no store resolves an empty map without failing", () => {
|
|
131
|
+
let eps = ReventlessSeedAws.endpointsFrom(~stack="pr-verify", StackOutputs(outputs(~extra=[])))
|
|
132
|
+
expect(eps.uploadEndpoint)->toBe("")
|
|
133
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual([])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
testSync("prefers the merged API endpoint over the per-plugin one", () => {
|
|
137
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
138
|
+
~stack="pr-verify",
|
|
139
|
+
StackOutputs(outputs(~extra=[("domainMergedApiEndpoint", str("https://merged.example/"))])),
|
|
140
|
+
)
|
|
141
|
+
expect(eps.graphql)->toBe("https://merged.example/")
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
testSync("fails naming both keys when the stack exports neither API endpoint", () =>
|
|
145
|
+
switch ReventlessSeedAws.endpointsFrom(
|
|
146
|
+
~stack="pr-verify",
|
|
147
|
+
StackOutputs(obj([("cognitoRegion", str("eu-west-1"))])),
|
|
148
|
+
) {
|
|
149
|
+
| _ => fail("expected endpointsFrom to fail")
|
|
150
|
+
| exception Seed.Failed(msg) =>
|
|
151
|
+
expect(msg)->toContain("domainMergedApiEndpoint")
|
|
152
|
+
expect(msg)->toContain("domainApiEndpoint")
|
|
153
|
+
}
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
describe("endpointsFrom — malformed uploadEndpoints", () => {
|
|
158
|
+
beforeEach(clearOverrides)
|
|
159
|
+
|
|
160
|
+
// One bad member should not cost the run every endpoint it could have used.
|
|
161
|
+
testSync("drops a non-string member rather than the whole map", () => {
|
|
162
|
+
let eps = ReventlessSeedAws.endpointsFrom(
|
|
163
|
+
~stack="pr-verify",
|
|
164
|
+
StackOutputs(
|
|
165
|
+
outputs(
|
|
166
|
+
~extra=[
|
|
167
|
+
(
|
|
168
|
+
"uploadEndpoints",
|
|
169
|
+
obj([
|
|
170
|
+
("Catalog.productImages", str("https://presign.example/")),
|
|
171
|
+
("Catalog.broken", JSON.Encode.int(7)),
|
|
172
|
+
]),
|
|
173
|
+
),
|
|
174
|
+
],
|
|
175
|
+
),
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
expect(eps.uploadEndpoints->Dict.keysToArray)->toEqual(["Catalog.productImages"])
|
|
179
|
+
})
|
|
180
|
+
})
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|