@reventlessdev/reventless-seed 1.0.0-alpha.6 → 1.0.0-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/lib/bs/.compiler.log +2 -2
  3. package/lib/bs/ReventlessSeed.cmi +0 -0
  4. package/lib/bs/ReventlessSeed.cmt +0 -0
  5. package/lib/bs/ReventlessSeed.mlmap +1 -1
  6. package/lib/bs/compiler-info.json +1 -1
  7. package/lib/bs/src/Seed-ReventlessSeed.cmi +0 -0
  8. package/lib/bs/src/Seed-ReventlessSeed.cmt +0 -0
  9. package/lib/bs/src/Seed.ast +0 -0
  10. package/lib/bs/src/Seed.res +4 -5
  11. package/lib/bs/src/Seed_Client-ReventlessSeed.cmi +0 -0
  12. package/lib/bs/src/Seed_Client-ReventlessSeed.cmj +0 -0
  13. package/lib/bs/src/Seed_Client-ReventlessSeed.cmt +0 -0
  14. package/lib/bs/src/Seed_Client.ast +0 -0
  15. package/lib/bs/src/Seed_Client.res +4 -0
  16. package/lib/bs/src/Seed_Client.res.mjs +5 -0
  17. package/lib/bs/src/Seed_Connect-ReventlessSeed.cmi +0 -0
  18. package/lib/bs/src/Seed_Connect-ReventlessSeed.cmj +0 -0
  19. package/lib/bs/src/Seed_Connect-ReventlessSeed.cmt +0 -0
  20. package/lib/bs/src/Seed_Connect.ast +0 -0
  21. package/lib/bs/src/Seed_Connect.res +13 -49
  22. package/lib/bs/src/Seed_Connect.res.mjs +5 -26
  23. package/lib/bs/src/Seed_Prompt-ReventlessSeed.cmt +0 -0
  24. package/lib/bs/src/Seed_Runner-ReventlessSeed.cmi +0 -0
  25. package/lib/bs/src/Seed_Runner-ReventlessSeed.cmt +0 -0
  26. package/lib/bs/src/Seed_Upload-ReventlessSeed.cmi +0 -0
  27. package/lib/bs/src/Seed_Upload-ReventlessSeed.cmj +0 -0
  28. package/lib/bs/src/Seed_Upload-ReventlessSeed.cmt +0 -0
  29. package/lib/bs/src/Seed_Upload.ast +0 -0
  30. package/lib/bs/src/Seed_Upload.res +39 -100
  31. package/lib/bs/src/Seed_Upload.res.mjs +61 -93
  32. package/lib/bs/tests/Seed_RandomTest-ReventlessSeed.cmi +0 -0
  33. package/lib/bs/tests/Seed_RandomTest-ReventlessSeed.cmj +0 -0
  34. package/lib/bs/tests/Seed_RandomTest-ReventlessSeed.cmt +0 -0
  35. package/lib/bs/tests/Seed_RandomTest.ast +0 -0
  36. package/lib/bs/tests/Seed_RandomTest.res +160 -0
  37. package/lib/bs/tests/Seed_RandomTest.res.mjs +208 -0
  38. package/lib/ocaml/.compiler.log +2 -2
  39. package/lib/ocaml/ReventlessSeed.cmi +0 -0
  40. package/lib/ocaml/ReventlessSeed.cmt +0 -0
  41. package/lib/ocaml/ReventlessSeed.mlmap +1 -1
  42. package/lib/ocaml/Seed-ReventlessSeed.cmi +0 -0
  43. package/lib/ocaml/Seed-ReventlessSeed.cmt +0 -0
  44. package/lib/ocaml/Seed.ast +0 -0
  45. package/lib/ocaml/Seed.res +4 -5
  46. package/lib/ocaml/Seed_Client-ReventlessSeed.cmi +0 -0
  47. package/lib/ocaml/Seed_Client-ReventlessSeed.cmj +0 -0
  48. package/lib/ocaml/Seed_Client-ReventlessSeed.cmt +0 -0
  49. package/lib/ocaml/Seed_Client.ast +0 -0
  50. package/lib/ocaml/Seed_Client.res +4 -0
  51. package/lib/ocaml/Seed_Connect-ReventlessSeed.cmi +0 -0
  52. package/lib/ocaml/Seed_Connect-ReventlessSeed.cmj +0 -0
  53. package/lib/ocaml/Seed_Connect-ReventlessSeed.cmt +0 -0
  54. package/lib/ocaml/Seed_Connect.ast +0 -0
  55. package/lib/ocaml/Seed_Connect.res +13 -49
  56. package/lib/ocaml/Seed_Prompt-ReventlessSeed.cmt +0 -0
  57. package/lib/ocaml/Seed_RandomTest-ReventlessSeed.cmi +0 -0
  58. package/lib/ocaml/Seed_RandomTest-ReventlessSeed.cmj +0 -0
  59. package/lib/ocaml/Seed_RandomTest-ReventlessSeed.cmt +0 -0
  60. package/lib/ocaml/Seed_RandomTest.ast +0 -0
  61. package/lib/ocaml/Seed_RandomTest.res +160 -0
  62. package/lib/ocaml/Seed_Runner-ReventlessSeed.cmi +0 -0
  63. package/lib/ocaml/Seed_Runner-ReventlessSeed.cmt +0 -0
  64. package/lib/ocaml/Seed_Upload-ReventlessSeed.cmi +0 -0
  65. package/lib/ocaml/Seed_Upload-ReventlessSeed.cmj +0 -0
  66. package/lib/ocaml/Seed_Upload-ReventlessSeed.cmt +0 -0
  67. package/lib/ocaml/Seed_Upload.ast +0 -0
  68. package/lib/ocaml/Seed_Upload.res +39 -100
  69. package/package.json +2 -2
  70. package/src/Seed.res +4 -5
  71. package/src/Seed_Client.res +4 -0
  72. package/src/Seed_Client.res.mjs +5 -0
  73. package/src/Seed_Connect.res +13 -49
  74. package/src/Seed_Connect.res.mjs +5 -26
  75. package/src/Seed_Upload.res +39 -100
  76. package/src/Seed_Upload.res.mjs +61 -93
  77. package/tests/Seed_RandomTest.res +160 -0
  78. package/tests/Seed_RandomTest.res.mjs +208 -0
  79. package/lib/bs/tests/UploadEndpointResolutionTest-ReventlessSeed.cmi +0 -0
  80. package/lib/bs/tests/UploadEndpointResolutionTest-ReventlessSeed.cmj +0 -0
  81. package/lib/bs/tests/UploadEndpointResolutionTest-ReventlessSeed.cmt +0 -0
  82. package/lib/bs/tests/UploadEndpointResolutionTest.ast +0 -0
  83. package/lib/bs/tests/UploadEndpointResolutionTest.res +0 -100
  84. package/lib/bs/tests/UploadEndpointResolutionTest.res.mjs +0 -59
  85. package/lib/ocaml/UploadEndpointResolutionTest-ReventlessSeed.cmi +0 -0
  86. package/lib/ocaml/UploadEndpointResolutionTest-ReventlessSeed.cmj +0 -0
  87. package/lib/ocaml/UploadEndpointResolutionTest-ReventlessSeed.cmt +0 -0
  88. package/lib/ocaml/UploadEndpointResolutionTest.ast +0 -0
  89. package/lib/ocaml/UploadEndpointResolutionTest.res +0 -100
  90. package/tests/UploadEndpointResolutionTest.res +0 -100
  91. package/tests/UploadEndpointResolutionTest.res.mjs +0 -59
@@ -0,0 +1,208 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
4
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
5
+ import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
6
+ import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
7
+ import * as Seed_Random$ReventlessSeed from "../src/Seed_Random.res.mjs";
8
+
9
+ globalThis.describe("Seed_Random:", () => {
10
+ let take = (seed, n) => {
11
+ let r = Seed_Random$ReventlessSeed.make(seed);
12
+ return Stdlib_Array.make(n, 0.0).map(param => Seed_Random$ReventlessSeed.float(r));
13
+ };
14
+ globalThis.describe("reproducibility — the reason this module exists:", () => {
15
+ globalThis.test("the same seed replays the same sequence", () => {
16
+ globalThis.expect(take(42, 25)).toEqual(take(42, 25));
17
+ });
18
+ globalThis.test("different seeds diverge", () => {
19
+ globalThis.expect(Primitive_object.equal(take(42, 25), take(43, 25))).toBe(false);
20
+ });
21
+ globalThis.test("a fresh generator does not continue the previous one", () => {
22
+ let r = Seed_Random$ReventlessSeed.make(7);
23
+ let first = Seed_Random$ReventlessSeed.float(r);
24
+ Seed_Random$ReventlessSeed.float(r);
25
+ globalThis.expect(Seed_Random$ReventlessSeed.float(Seed_Random$ReventlessSeed.make(7))).toEqual(first);
26
+ });
27
+ });
28
+ globalThis.describe("float stays a unit interval:", () => {
29
+ globalThis.test("every draw is in [0, 1)", () => {
30
+ globalThis.expect(take(99, 500).every(f => {
31
+ if (f >= 0.0) {
32
+ return f < 1.0;
33
+ } else {
34
+ return false;
35
+ }
36
+ })).toBe(true);
37
+ });
38
+ globalThis.test("and the draws are not all the same value", () => {
39
+ let draws = take(99, 100);
40
+ globalThis.expect(draws.some(f => f !== draws[0])).toBe(true);
41
+ });
42
+ });
43
+ globalThis.describe("int is inclusive on both ends, as documented:", () => {
44
+ let draws = (min, max, n) => {
45
+ let r = Seed_Random$ReventlessSeed.make(5);
46
+ return Stdlib_Array.make(n, 0).map(param => Seed_Random$ReventlessSeed.int(r, min, max));
47
+ };
48
+ globalThis.test("never escapes the range", () => {
49
+ globalThis.expect(draws(3, 7, 500).every(i => {
50
+ if (i >= 3) {
51
+ return i <= 7;
52
+ } else {
53
+ return false;
54
+ }
55
+ })).toBe(true);
56
+ });
57
+ globalThis.test("reaches both bounds", () => {
58
+ let d = draws(3, 7, 500);
59
+ globalThis.expect([
60
+ d.includes(3),
61
+ d.includes(7)
62
+ ]).toEqual([
63
+ true,
64
+ true
65
+ ]);
66
+ });
67
+ globalThis.test("a single-value range is that value", () => {
68
+ globalThis.expect(draws(4, 4, 10).every(i => i === 4)).toBe(true);
69
+ });
70
+ });
71
+ globalThis.describe("pick:", () => {
72
+ globalThis.test("returns None for an empty array", () => {
73
+ globalThis.expect(Seed_Random$ReventlessSeed.pick(Seed_Random$ReventlessSeed.make(1), [])).toEqual(undefined);
74
+ });
75
+ globalThis.test("always lands on a member", () => {
76
+ let r = Seed_Random$ReventlessSeed.make(2);
77
+ let xs = [
78
+ "a",
79
+ "b",
80
+ "c",
81
+ "d"
82
+ ];
83
+ let hits = Stdlib_Array.make(200, "").map(param => Seed_Random$ReventlessSeed.pickOr(r, "MISS", xs));
84
+ globalThis.expect(hits.every(h => xs.includes(h))).toBe(true);
85
+ });
86
+ globalThis.test("pickOr falls back only when there is nothing to pick", () => {
87
+ globalThis.expect(Seed_Random$ReventlessSeed.pickOr(Seed_Random$ReventlessSeed.make(3), "fallback", [])).toBe("fallback");
88
+ });
89
+ });
90
+ globalThis.describe("sampleWeighted is without replacement:", () => {
91
+ let equal = xs => xs.map(x => [
92
+ x,
93
+ 1.0
94
+ ]);
95
+ globalThis.test("count = length yields a permutation, not a multiset", () => {
96
+ let xs = [
97
+ "a",
98
+ "b",
99
+ "c",
100
+ "d",
101
+ "e"
102
+ ];
103
+ let out = Seed_Random$ReventlessSeed.sampleWeighted(Seed_Random$ReventlessSeed.make(11), equal(xs), 5);
104
+ let sorted = out.toSorted(Primitive_string.compare);
105
+ globalThis.expect([
106
+ out.length,
107
+ sorted
108
+ ]).toEqual([
109
+ 5,
110
+ [
111
+ "a",
112
+ "b",
113
+ "c",
114
+ "d",
115
+ "e"
116
+ ]
117
+ ]);
118
+ });
119
+ globalThis.test("count < length returns exactly count distinct items", () => {
120
+ let xs = [
121
+ "a",
122
+ "b",
123
+ "c",
124
+ "d",
125
+ "e"
126
+ ];
127
+ let out = Seed_Random$ReventlessSeed.sampleWeighted(Seed_Random$ReventlessSeed.make(12), equal(xs), 3);
128
+ let distinct = new Set(out).size;
129
+ globalThis.expect([
130
+ out.length,
131
+ distinct
132
+ ]).toEqual([
133
+ 3,
134
+ 3
135
+ ]);
136
+ });
137
+ globalThis.test("count > length clamps to the pool", () => {
138
+ globalThis.expect(Seed_Random$ReventlessSeed.sampleWeighted(Seed_Random$ReventlessSeed.make(13), equal([
139
+ "a",
140
+ "b"
141
+ ]), 99).length).toBe(2);
142
+ });
143
+ globalThis.test("a zero-weight entry is never chosen while others remain", () => {
144
+ let out = Seed_Random$ReventlessSeed.sampleWeighted(Seed_Random$ReventlessSeed.make(14), [
145
+ [
146
+ "never",
147
+ 0.0
148
+ ],
149
+ [
150
+ "a",
151
+ 1.0
152
+ ],
153
+ [
154
+ "b",
155
+ 1.0
156
+ ]
157
+ ], 2);
158
+ globalThis.expect(out.includes("never")).toBe(false);
159
+ });
160
+ globalThis.test("is reproducible for a given seed", () => {
161
+ let run = () => Seed_Random$ReventlessSeed.sampleWeighted(Seed_Random$ReventlessSeed.make(15), equal([
162
+ "a",
163
+ "b",
164
+ "c",
165
+ "d",
166
+ "e",
167
+ "f"
168
+ ]), 4);
169
+ globalThis.expect(run()).toEqual(run());
170
+ });
171
+ });
172
+ globalThis.describe("zipfWeights skews toward the head:", () => {
173
+ let weights = Seed_Random$ReventlessSeed.zipfWeights([
174
+ "a",
175
+ "b",
176
+ "c",
177
+ "d",
178
+ "e"
179
+ ], 1.1);
180
+ globalThis.test("keeps every item, in order", () => {
181
+ globalThis.expect(weights.map(param => param[0])).toEqual([
182
+ "a",
183
+ "b",
184
+ "c",
185
+ "d",
186
+ "e"
187
+ ]);
188
+ });
189
+ globalThis.test("weight decreases monotonically with rank", () => {
190
+ let ws = weights.map(param => param[1]);
191
+ let decreasing = ws.every((w, i) => {
192
+ if (i === 0) {
193
+ return true;
194
+ } else {
195
+ return w < ws[i - 1 | 0];
196
+ }
197
+ });
198
+ globalThis.expect(decreasing).toBe(true);
199
+ });
200
+ globalThis.test("the leader is clear — first outweighs last several times over", () => {
201
+ let first = Stdlib_Option.mapOr(weights[0], 0.0, param => param[1]);
202
+ let last = Stdlib_Option.mapOr(weights[4], 0.0, param => param[1]);
203
+ globalThis.expect(first > last * 3.0).toBe(true);
204
+ });
205
+ });
206
+ });
207
+
208
+ /* Not a pure module */
@@ -1,100 +0,0 @@
1
- // Which endpoint an asset uploads through once a deployment publishes one per
2
- // declared store, and what a data set is told when none resolves.
3
- //
4
- // The four rows are the same ones the AutoUI renderer resolves an upload field
5
- // by. They are pinned here because the two middle ones are what keep a
6
- // deployment predating per-store endpoints — and the local dev server, which
7
- // serves a single upload route — working unchanged, and because the row that
8
- // falls back to the legacy service must never fall back to *another store*: that
9
- // uploads into a different plugin's bucket with a 2xx and a plausible ref.
10
-
11
- open JestGlobals
12
-
13
-
14
- // `unresolvedReason` reads the knob, so the ambient environment has to be quiet.
15
- let clearSkip = () => NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "")
16
-
17
- let perStore = Dict.fromArray([
18
- ("Catalog.productImages", "https://catalog.example/"),
19
- ("Ordering.invoices", "https://ordering.example/"),
20
- ])
21
-
22
- describe("Seed.Upload.endpointFor", () => {
23
- testSync("a declared store with a matching endpoint uses that store's endpoint", () =>
24
- expect(
25
- Seed.Upload.endpointFor(
26
- ~store="Catalog.productImages",
27
- ~uploadEndpoint="https://legacy.example/",
28
- ~uploadEndpoints=perStore,
29
- ),
30
- )->toEqual(Some("https://catalog.example/"))
31
- )
32
-
33
- // Not "https://ordering.example/" — a wrong guess here is a silent
34
- // wrong-bucket write.
35
- testSync("a declared store with no matching endpoint falls back to the legacy service", () =>
36
- expect(
37
- Seed.Upload.endpointFor(
38
- ~store="Catalog.brochures",
39
- ~uploadEndpoint="https://legacy.example/",
40
- ~uploadEndpoints=perStore,
41
- ),
42
- )->toEqual(Some("https://legacy.example/"))
43
- )
44
-
45
- testSync("naming no store uses the legacy service", () =>
46
- expect(
47
- Seed.Upload.endpointFor(~uploadEndpoint="https://legacy.example/", ~uploadEndpoints=perStore),
48
- )->toEqual(Some("https://legacy.example/"))
49
- )
50
-
51
- testSync("neither available resolves nothing", () =>
52
- expect(
53
- Seed.Upload.endpointFor(
54
- ~store="Catalog.brochures",
55
- ~uploadEndpoint="",
56
- ~uploadEndpoints=Dict.make(),
57
- ),
58
- )->toEqual(None)
59
- )
60
-
61
- // The local dev server's shape: one upload route, no per-store map, and a data
62
- // set that names its store all the same.
63
- testSync("a single-route deployment serves a store-naming data set", () =>
64
- expect(
65
- Seed.Upload.endpointFor(
66
- ~store="Catalog.productImages",
67
- ~uploadEndpoint="http://localhost:4000/__inmemory/upload",
68
- ~uploadEndpoints=Dict.make(),
69
- ),
70
- )->toEqual(Some("http://localhost:4000/__inmemory/upload"))
71
- )
72
- })
73
-
74
- // "no upload endpoint" states a fact about the deployment. When the deployment
75
- // publishes endpoints the caller could not match, the fact is about the client,
76
- // and that phrasing sends a reader to the bucket, the presign service and the
77
- // IAM policy — all of which are correct.
78
- describe("Seed.Upload.unresolvedReason", () => {
79
- beforeEach(clearSkip)
80
-
81
- testSync("no endpoints at all reads as a fact about the deployment", () =>
82
- expect(
83
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=Dict.make()),
84
- )->toBe("this deployment publishes no upload endpoint")
85
- )
86
-
87
- testSync("an unmatched store names the store and the keys that were available", () => {
88
- let msg = Seed.Upload.unresolvedReason(~store="Catalog.brochures", ~uploadEndpoints=perStore)
89
- expect(msg)->toContain("Catalog.brochures")
90
- expect(msg)->toContain("Catalog.productImages")
91
- expect(msg)->toContain("Ordering.invoices")
92
- })
93
-
94
- testSync("the knob outranks both, so a skipped run is never read as a broken one", () => {
95
- NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "1")
96
- expect(
97
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=perStore),
98
- )->toBe("SEED_SKIP_UPLOADS is set")
99
- })
100
- })
@@ -1,59 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Seed_Upload$ReventlessSeed from "../src/Seed_Upload.res.mjs";
4
-
5
- function clearSkip() {
6
- process.env["SEED_SKIP_UPLOADS"] = "";
7
- }
8
-
9
- let perStore = Object.fromEntries([
10
- [
11
- "Catalog.productImages",
12
- "https://catalog.example/"
13
- ],
14
- [
15
- "Ordering.invoices",
16
- "https://ordering.example/"
17
- ]
18
- ]);
19
-
20
- globalThis.describe("Seed.Upload.endpointFor", () => {
21
- globalThis.test("a declared store with a matching endpoint uses that store's endpoint", () => {
22
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.productImages", "https://legacy.example/", perStore)).toEqual("https://catalog.example/");
23
- });
24
- globalThis.test("a declared store with no matching endpoint falls back to the legacy service", () => {
25
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.brochures", "https://legacy.example/", perStore)).toEqual("https://legacy.example/");
26
- });
27
- globalThis.test("naming no store uses the legacy service", () => {
28
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor(undefined, "https://legacy.example/", perStore)).toEqual("https://legacy.example/");
29
- });
30
- globalThis.test("neither available resolves nothing", () => {
31
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.brochures", "", {})).toEqual(undefined);
32
- });
33
- globalThis.test("a single-route deployment serves a store-naming data set", () => {
34
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.productImages", "http://localhost:4000/__inmemory/upload", {})).toEqual("http://localhost:4000/__inmemory/upload");
35
- });
36
- });
37
-
38
- globalThis.describe("Seed.Upload.unresolvedReason", () => {
39
- globalThis.beforeEach(clearSkip);
40
- globalThis.test("no endpoints at all reads as a fact about the deployment", () => {
41
- globalThis.expect(Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.productImages", {})).toBe("this deployment publishes no upload endpoint");
42
- });
43
- globalThis.test("an unmatched store names the store and the keys that were available", () => {
44
- let msg = Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.brochures", perStore);
45
- globalThis.expect(msg).toContain("Catalog.brochures");
46
- globalThis.expect(msg).toContain("Catalog.productImages");
47
- globalThis.expect(msg).toContain("Ordering.invoices");
48
- });
49
- globalThis.test("the knob outranks both, so a skipped run is never read as a broken one", () => {
50
- process.env["SEED_SKIP_UPLOADS"] = "1";
51
- globalThis.expect(Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.productImages", perStore)).toBe("SEED_SKIP_UPLOADS is set");
52
- });
53
- });
54
-
55
- export {
56
- clearSkip,
57
- perStore,
58
- }
59
- /* perStore Not a pure module */
@@ -1,100 +0,0 @@
1
- // Which endpoint an asset uploads through once a deployment publishes one per
2
- // declared store, and what a data set is told when none resolves.
3
- //
4
- // The four rows are the same ones the AutoUI renderer resolves an upload field
5
- // by. They are pinned here because the two middle ones are what keep a
6
- // deployment predating per-store endpoints — and the local dev server, which
7
- // serves a single upload route — working unchanged, and because the row that
8
- // falls back to the legacy service must never fall back to *another store*: that
9
- // uploads into a different plugin's bucket with a 2xx and a plausible ref.
10
-
11
- open JestGlobals
12
-
13
-
14
- // `unresolvedReason` reads the knob, so the ambient environment has to be quiet.
15
- let clearSkip = () => NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "")
16
-
17
- let perStore = Dict.fromArray([
18
- ("Catalog.productImages", "https://catalog.example/"),
19
- ("Ordering.invoices", "https://ordering.example/"),
20
- ])
21
-
22
- describe("Seed.Upload.endpointFor", () => {
23
- testSync("a declared store with a matching endpoint uses that store's endpoint", () =>
24
- expect(
25
- Seed.Upload.endpointFor(
26
- ~store="Catalog.productImages",
27
- ~uploadEndpoint="https://legacy.example/",
28
- ~uploadEndpoints=perStore,
29
- ),
30
- )->toEqual(Some("https://catalog.example/"))
31
- )
32
-
33
- // Not "https://ordering.example/" — a wrong guess here is a silent
34
- // wrong-bucket write.
35
- testSync("a declared store with no matching endpoint falls back to the legacy service", () =>
36
- expect(
37
- Seed.Upload.endpointFor(
38
- ~store="Catalog.brochures",
39
- ~uploadEndpoint="https://legacy.example/",
40
- ~uploadEndpoints=perStore,
41
- ),
42
- )->toEqual(Some("https://legacy.example/"))
43
- )
44
-
45
- testSync("naming no store uses the legacy service", () =>
46
- expect(
47
- Seed.Upload.endpointFor(~uploadEndpoint="https://legacy.example/", ~uploadEndpoints=perStore),
48
- )->toEqual(Some("https://legacy.example/"))
49
- )
50
-
51
- testSync("neither available resolves nothing", () =>
52
- expect(
53
- Seed.Upload.endpointFor(
54
- ~store="Catalog.brochures",
55
- ~uploadEndpoint="",
56
- ~uploadEndpoints=Dict.make(),
57
- ),
58
- )->toEqual(None)
59
- )
60
-
61
- // The local dev server's shape: one upload route, no per-store map, and a data
62
- // set that names its store all the same.
63
- testSync("a single-route deployment serves a store-naming data set", () =>
64
- expect(
65
- Seed.Upload.endpointFor(
66
- ~store="Catalog.productImages",
67
- ~uploadEndpoint="http://localhost:4000/__inmemory/upload",
68
- ~uploadEndpoints=Dict.make(),
69
- ),
70
- )->toEqual(Some("http://localhost:4000/__inmemory/upload"))
71
- )
72
- })
73
-
74
- // "no upload endpoint" states a fact about the deployment. When the deployment
75
- // publishes endpoints the caller could not match, the fact is about the client,
76
- // and that phrasing sends a reader to the bucket, the presign service and the
77
- // IAM policy — all of which are correct.
78
- describe("Seed.Upload.unresolvedReason", () => {
79
- beforeEach(clearSkip)
80
-
81
- testSync("no endpoints at all reads as a fact about the deployment", () =>
82
- expect(
83
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=Dict.make()),
84
- )->toBe("this deployment publishes no upload endpoint")
85
- )
86
-
87
- testSync("an unmatched store names the store and the keys that were available", () => {
88
- let msg = Seed.Upload.unresolvedReason(~store="Catalog.brochures", ~uploadEndpoints=perStore)
89
- expect(msg)->toContain("Catalog.brochures")
90
- expect(msg)->toContain("Catalog.productImages")
91
- expect(msg)->toContain("Ordering.invoices")
92
- })
93
-
94
- testSync("the knob outranks both, so a skipped run is never read as a broken one", () => {
95
- NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "1")
96
- expect(
97
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=perStore),
98
- )->toBe("SEED_SKIP_UPLOADS is set")
99
- })
100
- })
@@ -1,100 +0,0 @@
1
- // Which endpoint an asset uploads through once a deployment publishes one per
2
- // declared store, and what a data set is told when none resolves.
3
- //
4
- // The four rows are the same ones the AutoUI renderer resolves an upload field
5
- // by. They are pinned here because the two middle ones are what keep a
6
- // deployment predating per-store endpoints — and the local dev server, which
7
- // serves a single upload route — working unchanged, and because the row that
8
- // falls back to the legacy service must never fall back to *another store*: that
9
- // uploads into a different plugin's bucket with a 2xx and a plausible ref.
10
-
11
- open JestGlobals
12
-
13
-
14
- // `unresolvedReason` reads the knob, so the ambient environment has to be quiet.
15
- let clearSkip = () => NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "")
16
-
17
- let perStore = Dict.fromArray([
18
- ("Catalog.productImages", "https://catalog.example/"),
19
- ("Ordering.invoices", "https://ordering.example/"),
20
- ])
21
-
22
- describe("Seed.Upload.endpointFor", () => {
23
- testSync("a declared store with a matching endpoint uses that store's endpoint", () =>
24
- expect(
25
- Seed.Upload.endpointFor(
26
- ~store="Catalog.productImages",
27
- ~uploadEndpoint="https://legacy.example/",
28
- ~uploadEndpoints=perStore,
29
- ),
30
- )->toEqual(Some("https://catalog.example/"))
31
- )
32
-
33
- // Not "https://ordering.example/" — a wrong guess here is a silent
34
- // wrong-bucket write.
35
- testSync("a declared store with no matching endpoint falls back to the legacy service", () =>
36
- expect(
37
- Seed.Upload.endpointFor(
38
- ~store="Catalog.brochures",
39
- ~uploadEndpoint="https://legacy.example/",
40
- ~uploadEndpoints=perStore,
41
- ),
42
- )->toEqual(Some("https://legacy.example/"))
43
- )
44
-
45
- testSync("naming no store uses the legacy service", () =>
46
- expect(
47
- Seed.Upload.endpointFor(~uploadEndpoint="https://legacy.example/", ~uploadEndpoints=perStore),
48
- )->toEqual(Some("https://legacy.example/"))
49
- )
50
-
51
- testSync("neither available resolves nothing", () =>
52
- expect(
53
- Seed.Upload.endpointFor(
54
- ~store="Catalog.brochures",
55
- ~uploadEndpoint="",
56
- ~uploadEndpoints=Dict.make(),
57
- ),
58
- )->toEqual(None)
59
- )
60
-
61
- // The local dev server's shape: one upload route, no per-store map, and a data
62
- // set that names its store all the same.
63
- testSync("a single-route deployment serves a store-naming data set", () =>
64
- expect(
65
- Seed.Upload.endpointFor(
66
- ~store="Catalog.productImages",
67
- ~uploadEndpoint="http://localhost:4000/__inmemory/upload",
68
- ~uploadEndpoints=Dict.make(),
69
- ),
70
- )->toEqual(Some("http://localhost:4000/__inmemory/upload"))
71
- )
72
- })
73
-
74
- // "no upload endpoint" states a fact about the deployment. When the deployment
75
- // publishes endpoints the caller could not match, the fact is about the client,
76
- // and that phrasing sends a reader to the bucket, the presign service and the
77
- // IAM policy — all of which are correct.
78
- describe("Seed.Upload.unresolvedReason", () => {
79
- beforeEach(clearSkip)
80
-
81
- testSync("no endpoints at all reads as a fact about the deployment", () =>
82
- expect(
83
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=Dict.make()),
84
- )->toBe("this deployment publishes no upload endpoint")
85
- )
86
-
87
- testSync("an unmatched store names the store and the keys that were available", () => {
88
- let msg = Seed.Upload.unresolvedReason(~store="Catalog.brochures", ~uploadEndpoints=perStore)
89
- expect(msg)->toContain("Catalog.brochures")
90
- expect(msg)->toContain("Catalog.productImages")
91
- expect(msg)->toContain("Ordering.invoices")
92
- })
93
-
94
- testSync("the knob outranks both, so a skipped run is never read as a broken one", () => {
95
- NodeProcess.env->Dict.set("SEED_SKIP_UPLOADS", "1")
96
- expect(
97
- Seed.Upload.unresolvedReason(~store="Catalog.productImages", ~uploadEndpoints=perStore),
98
- )->toBe("SEED_SKIP_UPLOADS is set")
99
- })
100
- })
@@ -1,59 +0,0 @@
1
- // Generated by ReScript, PLEASE EDIT WITH CARE
2
-
3
- import * as Seed_Upload$ReventlessSeed from "../src/Seed_Upload.res.mjs";
4
-
5
- function clearSkip() {
6
- process.env["SEED_SKIP_UPLOADS"] = "";
7
- }
8
-
9
- let perStore = Object.fromEntries([
10
- [
11
- "Catalog.productImages",
12
- "https://catalog.example/"
13
- ],
14
- [
15
- "Ordering.invoices",
16
- "https://ordering.example/"
17
- ]
18
- ]);
19
-
20
- globalThis.describe("Seed.Upload.endpointFor", () => {
21
- globalThis.test("a declared store with a matching endpoint uses that store's endpoint", () => {
22
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.productImages", "https://legacy.example/", perStore)).toEqual("https://catalog.example/");
23
- });
24
- globalThis.test("a declared store with no matching endpoint falls back to the legacy service", () => {
25
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.brochures", "https://legacy.example/", perStore)).toEqual("https://legacy.example/");
26
- });
27
- globalThis.test("naming no store uses the legacy service", () => {
28
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor(undefined, "https://legacy.example/", perStore)).toEqual("https://legacy.example/");
29
- });
30
- globalThis.test("neither available resolves nothing", () => {
31
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.brochures", "", {})).toEqual(undefined);
32
- });
33
- globalThis.test("a single-route deployment serves a store-naming data set", () => {
34
- globalThis.expect(Seed_Upload$ReventlessSeed.endpointFor("Catalog.productImages", "http://localhost:4000/__inmemory/upload", {})).toEqual("http://localhost:4000/__inmemory/upload");
35
- });
36
- });
37
-
38
- globalThis.describe("Seed.Upload.unresolvedReason", () => {
39
- globalThis.beforeEach(clearSkip);
40
- globalThis.test("no endpoints at all reads as a fact about the deployment", () => {
41
- globalThis.expect(Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.productImages", {})).toBe("this deployment publishes no upload endpoint");
42
- });
43
- globalThis.test("an unmatched store names the store and the keys that were available", () => {
44
- let msg = Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.brochures", perStore);
45
- globalThis.expect(msg).toContain("Catalog.brochures");
46
- globalThis.expect(msg).toContain("Catalog.productImages");
47
- globalThis.expect(msg).toContain("Ordering.invoices");
48
- });
49
- globalThis.test("the knob outranks both, so a skipped run is never read as a broken one", () => {
50
- process.env["SEED_SKIP_UPLOADS"] = "1";
51
- globalThis.expect(Seed_Upload$ReventlessSeed.unresolvedReason("Catalog.productImages", perStore)).toBe("SEED_SKIP_UPLOADS is set");
52
- });
53
- });
54
-
55
- export {
56
- clearSkip,
57
- perStore,
58
- }
59
- /* perStore Not a pure module */