@reventlessdev/reventless-local 3.0.0-alpha.297 → 3.0.0-alpha.299
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 +36 -0
- package/package.json +10 -10
- package/src/EmitCapabilities.res +3 -3
- package/src/LocalPlatformRegistry.res +1 -1
- package/src/LocalSeedTarget.res +1 -1
- package/src/Platform.res +57 -3
- package/src/Platform.res.mjs +61 -6
- package/src/ShellConfig.res +11 -1
- package/src/ShellConfig.res.mjs +10 -4
- package/src/adapter/Auth/Auth_GraphqlContext.res +18 -2
- package/src/adapter/Auth/Auth_GraphqlContext.res.mjs +8 -1
- package/src/adapter/Auth/UserStore.res +102 -3
- package/src/adapter/Auth/UserStore.res.mjs +128 -2
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +33 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +26 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +11 -3
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res.mjs +3 -0
- package/src/adapter/DomainGraphQL_Server.res +1 -1
- package/src/adapter/GraphQL_CallerError.res +4 -0
- package/src/adapter/GraphQL_CallerError.res.mjs +9 -0
- package/src/adapter/LocalAnonymousLimit.res +35 -0
- package/src/adapter/LocalAnonymousLimit.res.mjs +52 -0
- package/src/adapter/LocalCapabilities.res +14 -2
- package/src/adapter/LocalCapabilities.res.mjs +21 -5
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +22 -0
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +7 -0
- package/src/capability/Capability_IdentityProvider_UserStore.res +6 -2
- package/src/capability/Capability_IdentityProvider_UserStore.res.mjs +6 -2
- package/src/components/InboundTranslationSlice_Builder.res +1 -0
- package/src/components/InboundTranslationSlice_Builder.res.mjs +4 -1
- package/tests/EmitCapabilitiesTest.res +4 -6
- package/tests/EmitCapabilitiesTest.res.mjs +1 -2
- package/tests/HostShellDistTest.res.mjs +1 -1
- package/tests/LocalPlatformRegistryTest.res +3 -5
- package/tests/LocalPlatformRegistryTest.res.mjs +2 -4
- package/tests/LocalPlatformStartTest.res +2 -2
- package/tests/LocalPlatformStartTest.res.mjs +1 -2
- package/tests/ShellConfigTest.res.mjs +27 -27
- package/tests/adapter/InboundTranslationMutationTest.res +13 -2
- package/tests/adapter/InboundTranslationMutationTest.res.mjs +7 -4
- package/tests/adapter/LocalAnonymousLimitTest.res +70 -0
- package/tests/adapter/LocalAnonymousLimitTest.res.mjs +85 -0
- package/tests/adapter/LocalAuthLoginTest.res +2 -2
- package/tests/adapter/LocalAuthUserStoreTest.res +88 -0
- package/tests/adapter/LocalAuthUserStoreTest.res.mjs +83 -0
- package/tests/capability/Capability_IdentityProvider_UserStoreTest.res +6 -0
- package/tests/capability/Capability_IdentityProvider_UserStoreTest.res.mjs +4 -0
- package/tests/capability/LocalCapabilitiesTest.res +2 -1
- package/tests/capability/LocalCapabilitiesTest.res.mjs +3 -2
- package/tests/components/inboundtranslationslice/InboundTranslationCredentialTest.res +136 -0
- package/tests/components/inboundtranslationslice/InboundTranslationCredentialTest.res.mjs +183 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +143 -11
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +129 -22
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +18 -5
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +12 -3
- package/tests/components/inboundtranslationslice/InboundTranslationWebhookTest.res +26 -6
- package/tests/components/inboundtranslationslice/InboundTranslationWebhookTest.res.mjs +48 -13
- package/tests/plugin/Plugin_Fixtures.res +0 -4
- package/tests/plugin/Plugin_Fixtures.res.mjs +13 -33
|
@@ -56,9 +56,13 @@ let make = (~path: option<string>=?, ()): IdentityProvider.t => {
|
|
|
56
56
|
| Ok(username) if LocalAuth.Login.credentialsOf(~username)->Option.isSome => Error(Conflict)
|
|
57
57
|
| Ok(username) =>
|
|
58
58
|
let password = passwordOf(credential)
|
|
59
|
+
// Minted here, never the username: the id reaches the event log as
|
|
60
|
+
// `meta.user` and every `@owner` field, and a log keyed by login names
|
|
61
|
+
// cannot survive a rename or a move to another provider.
|
|
62
|
+
let userId = ReventlessCore.Auth_UserId.mint()
|
|
59
63
|
switch AccountsManifest.editFile(
|
|
60
64
|
~path=file(),
|
|
61
|
-
~edits=[Append({username, password, groups})],
|
|
65
|
+
~edits=[Append({username, password, groups, userId})],
|
|
62
66
|
) {
|
|
63
67
|
| Error(message) if message->String.endsWith("already declared in the manifest") =>
|
|
64
68
|
Error(Conflict)
|
|
@@ -67,7 +71,7 @@ let make = (~path: option<string>=?, ()): IdentityProvider.t => {
|
|
|
67
71
|
LocalAuth.Login.setCredentials(
|
|
68
72
|
~username,
|
|
69
73
|
~password,
|
|
70
|
-
~identity=identityOf(~username, ~groups),
|
|
74
|
+
~identity={...identityOf(~username, ~groups), userId},
|
|
71
75
|
)
|
|
72
76
|
Ok({IdentityProvider.providerId: username})
|
|
73
77
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
4
|
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
5
5
|
import * as LocalAuth$ReventlessLocal from "../adapter/Auth/LocalAuth.res.mjs";
|
|
6
|
+
import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
|
|
6
7
|
import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
|
|
7
8
|
import * as IdentityProvider$Reventless from "@reventlessdev/reventless-spec/src/semantic/IdentityProvider.res.mjs";
|
|
8
9
|
|
|
@@ -60,16 +61,19 @@ function make(path, param) {
|
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
63
|
let password = passwordOf(credential);
|
|
64
|
+
let userId = Auth_UserId$ReventlessCore.mint();
|
|
63
65
|
let message = AccountsManifest$Reventless.editFile(Stdlib_Option.getOr(path, AccountsManifest$Reventless.defaultPath()), [{
|
|
64
66
|
TAG: "Append",
|
|
65
67
|
_0: {
|
|
66
68
|
username: username,
|
|
67
69
|
password: password,
|
|
68
|
-
groups: groups
|
|
70
|
+
groups: groups,
|
|
71
|
+
userId: userId
|
|
69
72
|
}
|
|
70
73
|
}]);
|
|
71
74
|
if (message.TAG === "Ok") {
|
|
72
|
-
|
|
75
|
+
let newrecord = {...identityOf(username, groups)};
|
|
76
|
+
LocalAuth$ReventlessLocal.Login.setCredentials(username, password, (newrecord.userId = userId, newrecord));
|
|
73
77
|
return {
|
|
74
78
|
TAG: "Ok",
|
|
75
79
|
_0: {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
4
|
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
5
|
+
import * as LocalCapabilities$ReventlessLocal from "../adapter/LocalCapabilities.res.mjs";
|
|
5
6
|
import * as LocalQueryDbStorage$ReventlessLocal from "../adapter/QueryDb/LocalQueryDbStorage.res.mjs";
|
|
6
7
|
import * as LocalWebhookRegistry$ReventlessLocal from "../adapter/Webhook/LocalWebhookRegistry.res.mjs";
|
|
7
8
|
import * as QueryDbResolvers_GraphQL$ReventlessLocal from "../adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs";
|
|
@@ -21,7 +22,9 @@ function Make(Bus) {
|
|
|
21
22
|
make: QueryDbStorage.make
|
|
22
23
|
})({
|
|
23
24
|
make: QueryDbResolvers.make
|
|
24
|
-
})(Api)
|
|
25
|
+
})(Api)({
|
|
26
|
+
capabilities: LocalCapabilities$ReventlessLocal.capabilities
|
|
27
|
+
});
|
|
25
28
|
let Make$1 = Spec => (Translation => {
|
|
26
29
|
let Inner = CoreMaker.Make(Spec)(Translation);
|
|
27
30
|
let make = (publishJsons, runtime, opts) => {
|
|
@@ -5,14 +5,12 @@ open JestGlobals
|
|
|
5
5
|
|
|
6
6
|
describe("EmitCapabilities.parseArgs", () => {
|
|
7
7
|
testSync("a plugin's postbuild: emit-capabilities src/", () =>
|
|
8
|
-
expect(EmitCapabilities.parseArgs(["src/"]))->toEqual(
|
|
9
|
-
Ok({EmitCapabilities.srcDir: "src/", compositionModule: None}),
|
|
10
|
-
)
|
|
8
|
+
expect(EmitCapabilities.parseArgs(["src/"]))->toEqual(Ok({EmitCapabilities.srcDir: "src/"}))
|
|
11
9
|
)
|
|
12
10
|
|
|
13
11
|
testSync("a hand-written composition root is named after the source directory", () =>
|
|
14
12
|
expect(EmitCapabilities.parseArgs(["src", "Catalog"]))->toEqual(
|
|
15
|
-
Ok({EmitCapabilities.srcDir: "src", compositionModule:
|
|
13
|
+
Ok({EmitCapabilities.srcDir: "src", compositionModule: "Catalog"}),
|
|
16
14
|
)
|
|
17
15
|
)
|
|
18
16
|
|
|
@@ -29,7 +27,7 @@ describe("emit-capabilities on the command line", () => {
|
|
|
29
27
|
expect(await CliArgs.observe(EmitCapabilities.cli, ["src/", "--help"]))->toEqual({
|
|
30
28
|
CliArgs.stdout: [usage],
|
|
31
29
|
stderr: [],
|
|
32
|
-
exitCode:
|
|
30
|
+
exitCode: 0,
|
|
33
31
|
reachedMain: false,
|
|
34
32
|
})
|
|
35
33
|
)
|
|
@@ -38,7 +36,7 @@ describe("emit-capabilities on the command line", () => {
|
|
|
38
36
|
expect(await CliArgs.observe(EmitCapabilities.cli, ["src", "Catalog", "extra"]))->toEqual({
|
|
39
37
|
CliArgs.stdout: [],
|
|
40
38
|
stderr: [`emit-capabilities: unknown argument "extra"\n\n${usage}`],
|
|
41
|
-
exitCode:
|
|
39
|
+
exitCode: 2,
|
|
42
40
|
reachedMain: false,
|
|
43
41
|
})
|
|
44
42
|
)
|
|
@@ -53,7 +53,7 @@ globalThis.describe("the local writers in a chosen assetsDir", () => {
|
|
|
53
53
|
let bake = {
|
|
54
54
|
components: bake_components
|
|
55
55
|
};
|
|
56
|
-
ShellConfig$ReventlessLocal.emit(bake, undefined, undefined, dir);
|
|
56
|
+
ShellConfig$ReventlessLocal.emit(bake, undefined, undefined, undefined, dir);
|
|
57
57
|
UiHints$ReventlessLocal.emit(source("ui-hints.json"), dir);
|
|
58
58
|
UiSlots$ReventlessLocal.emit(source("slots.js"), dir);
|
|
59
59
|
BakedManifest$ReventlessLocal.emit([], bake, dir);
|
|
@@ -17,8 +17,8 @@ let tempRoot = (): string =>
|
|
|
17
17
|
// rather than hitting an unrelated process that happens to be running.
|
|
18
18
|
let deadPid = 2147483646
|
|
19
19
|
|
|
20
|
-
let sqliteStore = (path): LocalPlatformRegistry.store => {kind: "sqlite", path
|
|
21
|
-
let memoryStore: LocalPlatformRegistry.store = {kind: "memory"
|
|
20
|
+
let sqliteStore = (path): LocalPlatformRegistry.store => {kind: "sqlite", path}
|
|
21
|
+
let memoryStore: LocalPlatformRegistry.store = {kind: "memory"}
|
|
22
22
|
|
|
23
23
|
let writeAt = (~cwd, ~port, ~store, ~pid=NodeProcess.pid) =>
|
|
24
24
|
LocalPlatformRegistry.write(
|
|
@@ -127,9 +127,7 @@ describe("LocalSeedTarget.storePath", () => {
|
|
|
127
127
|
|
|
128
128
|
expect(target(memoryStore)->reason->String.includes("in memory"))->toEqual(true)
|
|
129
129
|
expect(target(memoryStore)->reason->String.includes("localhost:4000"))->toEqual(true)
|
|
130
|
-
expect(
|
|
131
|
-
target({kind: "postgres", path: None})->reason->String.includes("off this machine"),
|
|
132
|
-
)->toEqual(true)
|
|
130
|
+
expect(target({kind: "postgres"})->reason->String.includes("off this machine"))->toEqual(true)
|
|
133
131
|
expect(
|
|
134
132
|
{
|
|
135
133
|
endpoint: "http://localhost:4000/graphql",
|
|
@@ -19,8 +19,7 @@ function sqliteStore(path) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
let memoryStore = {
|
|
22
|
-
kind: "memory"
|
|
23
|
-
path: undefined
|
|
22
|
+
kind: "memory"
|
|
24
23
|
};
|
|
25
24
|
|
|
26
25
|
function writeAt(cwd, port, store, pidOpt) {
|
|
@@ -129,8 +128,7 @@ globalThis.describe("LocalSeedTarget.storePath", () => {
|
|
|
129
128
|
globalThis.expect(reason(target(memoryStore)).includes("in memory")).toEqual(true);
|
|
130
129
|
globalThis.expect(reason(target(memoryStore)).includes("localhost:4000")).toEqual(true);
|
|
131
130
|
globalThis.expect(reason(target({
|
|
132
|
-
kind: "postgres"
|
|
133
|
-
path: undefined
|
|
131
|
+
kind: "postgres"
|
|
134
132
|
})).includes("off this machine")).toEqual(true);
|
|
135
133
|
globalThis.expect(reason({
|
|
136
134
|
endpoint: "http://localhost:4000/graphql",
|
|
@@ -10,8 +10,8 @@ let tempRoot = (): string =>
|
|
|
10
10
|
|
|
11
11
|
let deadPid = 2147483646
|
|
12
12
|
|
|
13
|
-
let sqliteStore = (path): LocalPlatformRegistry.store => {kind: "sqlite", path
|
|
14
|
-
let memoryStore: LocalPlatformRegistry.store = {kind: "memory"
|
|
13
|
+
let sqliteStore = (path): LocalPlatformRegistry.store => {kind: "sqlite", path}
|
|
14
|
+
let memoryStore: LocalPlatformRegistry.store = {kind: "memory"}
|
|
15
15
|
|
|
16
16
|
let writeAt = (~cwd, ~port, ~store, ~pid=NodeProcess.pid) =>
|
|
17
17
|
LocalPlatformRegistry.write(
|
|
@@ -37,14 +37,14 @@ function threw(f) {
|
|
|
37
37
|
|
|
38
38
|
globalThis.describe("ShellConfig.overlay", () => {
|
|
39
39
|
globalThis.test("carries only the environment when the deployment declares nothing", () => {
|
|
40
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
40
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined);
|
|
41
41
|
globalThis.expect(Object.keys(out)).toEqual([
|
|
42
42
|
"environment",
|
|
43
43
|
"environmentPurpose"
|
|
44
44
|
]);
|
|
45
45
|
});
|
|
46
46
|
globalThis.test("names the environment and its purpose local", () => {
|
|
47
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
47
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined);
|
|
48
48
|
globalThis.expect([
|
|
49
49
|
str(out, "environment"),
|
|
50
50
|
str(out, "environmentPurpose")
|
|
@@ -61,18 +61,18 @@ globalThis.describe("ShellConfig.overlay", () => {
|
|
|
61
61
|
ShellConfig$ReventlessLocal.overlay(undefined, undefined, Object.fromEntries([[
|
|
62
62
|
key,
|
|
63
63
|
"prod"
|
|
64
|
-
]]));
|
|
64
|
+
]]), undefined);
|
|
65
65
|
}))).toEqual([
|
|
66
66
|
true,
|
|
67
67
|
true
|
|
68
68
|
]);
|
|
69
69
|
});
|
|
70
70
|
globalThis.test("points manifestUrl at the bake's default key", () => {
|
|
71
|
-
let out = ShellConfig$ReventlessLocal.overlay(manifest(undefined), undefined, undefined);
|
|
71
|
+
let out = ShellConfig$ReventlessLocal.overlay(manifest(undefined), undefined, undefined, undefined);
|
|
72
72
|
globalThis.expect(str(out, "manifestUrl")).toEqual("/component-manifest.json");
|
|
73
73
|
});
|
|
74
74
|
globalThis.test("follows a renamed bake", () => {
|
|
75
|
-
let out = ShellConfig$ReventlessLocal.overlay(manifest("storefront.json"), undefined, undefined);
|
|
75
|
+
let out = ShellConfig$ReventlessLocal.overlay(manifest("storefront.json"), undefined, undefined, undefined);
|
|
76
76
|
globalThis.expect(str(out, "manifestUrl")).toEqual("/storefront.json");
|
|
77
77
|
});
|
|
78
78
|
globalThis.test("passes the deployment's own keys through verbatim", () => {
|
|
@@ -85,7 +85,7 @@ globalThis.describe("ShellConfig.overlay", () => {
|
|
|
85
85
|
"elevatedGroups",
|
|
86
86
|
["Admin"].map(prim => prim)
|
|
87
87
|
]
|
|
88
|
-
]));
|
|
88
|
+
]), undefined);
|
|
89
89
|
globalThis.expect(str(out, "appName")).toEqual("Online Shop");
|
|
90
90
|
globalThis.expect(Stdlib_Option.isSome(out["elevatedGroups"])).toBe(true);
|
|
91
91
|
});
|
|
@@ -94,22 +94,22 @@ globalThis.describe("ShellConfig.overlay", () => {
|
|
|
94
94
|
ShellConfig$ReventlessLocal.overlay(manifest(undefined), undefined, Object.fromEntries([[
|
|
95
95
|
"manifestUrl",
|
|
96
96
|
"/elsewhere.json"
|
|
97
|
-
]]));
|
|
97
|
+
]]), undefined);
|
|
98
98
|
})).toBe(true);
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
globalThis.describe("ShellConfig.overlay — uiSlotsFile", () => {
|
|
103
103
|
globalThis.test("unset ⇒ no uiSlotsUrl", () => {
|
|
104
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
104
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined);
|
|
105
105
|
globalThis.expect(Stdlib_Option.isNone(out["uiSlotsUrl"])).toBe(true);
|
|
106
106
|
});
|
|
107
107
|
globalThis.test("declared ⇒ the URL the platform serves the module from", () => {
|
|
108
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, "/src/storefront-slots.js", undefined);
|
|
108
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, "/src/storefront-slots.js", undefined, undefined);
|
|
109
109
|
globalThis.expect(out["uiSlotsUrl"]).toEqual("/ui-slots.js");
|
|
110
110
|
});
|
|
111
111
|
globalThis.test("withdrawn ⇒ the overlay stops naming it, so the baseline restores", () => {
|
|
112
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
112
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined);
|
|
113
113
|
globalThis.expect(Object.keys(out).includes("uiSlotsUrl")).toBe(false);
|
|
114
114
|
});
|
|
115
115
|
globalThis.test("refuses a shellConfig key redirecting the computed module", () => {
|
|
@@ -117,7 +117,7 @@ globalThis.describe("ShellConfig.overlay — uiSlotsFile", () => {
|
|
|
117
117
|
ShellConfig$ReventlessLocal.overlay(undefined, "/src/storefront-slots.js", Object.fromEntries([[
|
|
118
118
|
"uiSlotsUrl",
|
|
119
119
|
"/elsewhere.js"
|
|
120
|
-
]]));
|
|
120
|
+
]]), undefined);
|
|
121
121
|
})).toBe(true);
|
|
122
122
|
});
|
|
123
123
|
});
|
|
@@ -137,14 +137,14 @@ globalThis.describe("ShellConfig.emit", () => {
|
|
|
137
137
|
]]);
|
|
138
138
|
globalThis.test("merges the overlay onto the shipped file and keeps its other keys", () => {
|
|
139
139
|
let dir = distWithConfig();
|
|
140
|
-
ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, dir);
|
|
140
|
+
ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, undefined, dir);
|
|
141
141
|
let out = readConfig(dir);
|
|
142
142
|
globalThis.expect(str(out, "manifestUrl")).toEqual("/component-manifest.json");
|
|
143
143
|
globalThis.expect(str(out, "apiEndpoint")).toEqual("/graphql");
|
|
144
144
|
});
|
|
145
145
|
globalThis.test("adds only the environment for a platform that declares nothing", () => {
|
|
146
146
|
let dir = distWithConfig();
|
|
147
|
-
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, dir);
|
|
147
|
+
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, undefined, dir);
|
|
148
148
|
let out = readConfig(dir);
|
|
149
149
|
globalThis.expect(Object.keys(out)).toEqual([
|
|
150
150
|
"apiEndpoint",
|
|
@@ -156,27 +156,27 @@ globalThis.describe("ShellConfig.emit", () => {
|
|
|
156
156
|
});
|
|
157
157
|
globalThis.test("writes nothing and refuses nothing when only the environment has no baseline", () => {
|
|
158
158
|
let dir = Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), "reventless-shellconfig-"));
|
|
159
|
-
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, dir);
|
|
159
|
+
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, undefined, dir);
|
|
160
160
|
globalThis.expect(Nodefs.readdirSync(dir, {})).toEqual([]);
|
|
161
161
|
});
|
|
162
162
|
globalThis.test("starts every boot from the shipped file, not the last output", () => {
|
|
163
163
|
let dir = distWithConfig();
|
|
164
|
-
ShellConfig$ReventlessLocal.emit(undefined, undefined, named("First"), dir);
|
|
165
|
-
ShellConfig$ReventlessLocal.emit(undefined, undefined, named("Second"), dir);
|
|
164
|
+
ShellConfig$ReventlessLocal.emit(undefined, undefined, named("First"), undefined, dir);
|
|
165
|
+
ShellConfig$ReventlessLocal.emit(undefined, undefined, named("Second"), undefined, dir);
|
|
166
166
|
globalThis.expect(str(readConfig(dir), "appName")).toEqual("Second");
|
|
167
167
|
globalThis.expect(str(readBaseline(dir), "appName")).toEqual("Shipped");
|
|
168
168
|
});
|
|
169
169
|
globalThis.test("restores the shipped file once the declaration is withdrawn", () => {
|
|
170
170
|
let dir = distWithConfig();
|
|
171
|
-
ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, dir);
|
|
172
|
-
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, dir);
|
|
171
|
+
ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, undefined, dir);
|
|
172
|
+
ShellConfig$ReventlessLocal.emit(undefined, undefined, undefined, undefined, dir);
|
|
173
173
|
let out = readConfig(dir);
|
|
174
174
|
globalThis.expect(out["manifestUrl"]).toEqual(undefined);
|
|
175
175
|
globalThis.expect(str(out, "appName")).toEqual("Shipped");
|
|
176
176
|
});
|
|
177
177
|
globalThis.test("refuses to overlay a dist that ships no config.json", () => {
|
|
178
178
|
let dir = Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), "reventless-shellconfig-"));
|
|
179
|
-
globalThis.expect(threw(() => ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, dir))).toBe(true);
|
|
179
|
+
globalThis.expect(threw(() => ShellConfig$ReventlessLocal.emit(manifest(undefined), undefined, undefined, undefined, dir))).toBe(true);
|
|
180
180
|
});
|
|
181
181
|
});
|
|
182
182
|
|
|
@@ -218,22 +218,22 @@ let fulfilment = {
|
|
|
218
218
|
|
|
219
219
|
globalThis.describe("ShellConfig.overlay — journeys", () => {
|
|
220
220
|
globalThis.test("a bake declaring no journeys writes no map", () => {
|
|
221
|
-
let out = ShellConfig$ReventlessLocal.overlay(manifest(undefined), undefined, undefined);
|
|
221
|
+
let out = ShellConfig$ReventlessLocal.overlay(manifest(undefined), undefined, undefined, undefined);
|
|
222
222
|
globalThis.expect(out["journeyManifestUrls"]).toEqual(undefined);
|
|
223
223
|
});
|
|
224
224
|
globalThis.test("an empty journeys array is the same as none", () => {
|
|
225
|
-
let out = ShellConfig$ReventlessLocal.overlay(withJourneys([]), undefined, undefined);
|
|
225
|
+
let out = ShellConfig$ReventlessLocal.overlay(withJourneys([]), undefined, undefined, undefined);
|
|
226
226
|
globalThis.expect(out["journeyManifestUrls"]).toEqual(undefined);
|
|
227
227
|
});
|
|
228
228
|
globalThis.test("keeps manifestUrl as the default journey", () => {
|
|
229
|
-
let out = ShellConfig$ReventlessLocal.overlay(withJourneys([shopper]), undefined, undefined);
|
|
229
|
+
let out = ShellConfig$ReventlessLocal.overlay(withJourneys([shopper]), undefined, undefined, undefined);
|
|
230
230
|
globalThis.expect(str(out, "manifestUrl")).toEqual("/component-manifest.json");
|
|
231
231
|
});
|
|
232
232
|
globalThis.test("maps each declared group to its own file", () => {
|
|
233
233
|
let out = ShellConfig$ReventlessLocal.overlay(withJourneys([
|
|
234
234
|
shopper,
|
|
235
235
|
fulfilment
|
|
236
|
-
]), undefined, undefined);
|
|
236
|
+
]), undefined, undefined, undefined);
|
|
237
237
|
let map = Stdlib_Option.getOrThrow(Stdlib_Option.flatMap(out["journeyManifestUrls"], Stdlib_JSON.Decode.object), undefined);
|
|
238
238
|
globalThis.expect([
|
|
239
239
|
str(map, "Shopper"),
|
|
@@ -248,7 +248,7 @@ globalThis.describe("ShellConfig.overlay — journeys", () => {
|
|
|
248
248
|
ShellConfig$ReventlessLocal.overlay(withJourneys([shopper]), undefined, Object.fromEntries([[
|
|
249
249
|
"journeyManifestUrls",
|
|
250
250
|
{}
|
|
251
|
-
]]));
|
|
251
|
+
]]), undefined);
|
|
252
252
|
})).toBe(true);
|
|
253
253
|
});
|
|
254
254
|
});
|
|
@@ -256,7 +256,7 @@ globalThis.describe("ShellConfig.overlay — journeys", () => {
|
|
|
256
256
|
globalThis.describe("ShellConfig.overlay — adminGroup", () => {
|
|
257
257
|
globalThis.test("is absent while the administrator role keeps its own name", () => {
|
|
258
258
|
Role$Reventless.clearGroups();
|
|
259
|
-
globalThis.expect(ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined)["adminGroup"]).toEqual(undefined);
|
|
259
|
+
globalThis.expect(ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined)["adminGroup"]).toEqual(undefined);
|
|
260
260
|
});
|
|
261
261
|
globalThis.test("carries the group a renamed administrator role maps to", () => {
|
|
262
262
|
Role$Reventless.clearGroups();
|
|
@@ -264,7 +264,7 @@ globalThis.describe("ShellConfig.overlay — adminGroup", () => {
|
|
|
264
264
|
Role$Reventless.admin,
|
|
265
265
|
"shop-admins"
|
|
266
266
|
]]);
|
|
267
|
-
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined);
|
|
267
|
+
let out = ShellConfig$ReventlessLocal.overlay(undefined, undefined, undefined, undefined);
|
|
268
268
|
Role$Reventless.clearGroups();
|
|
269
269
|
globalThis.expect(str(out, "adminGroup")).toEqual("shop-admins");
|
|
270
270
|
});
|
|
@@ -273,7 +273,7 @@ globalThis.describe("ShellConfig.overlay — adminGroup", () => {
|
|
|
273
273
|
ShellConfig$ReventlessLocal.overlay(undefined, undefined, Object.fromEntries([[
|
|
274
274
|
"adminGroup",
|
|
275
275
|
"x"
|
|
276
|
-
]]));
|
|
276
|
+
]]), undefined);
|
|
277
277
|
})).toBe(true);
|
|
278
278
|
});
|
|
279
279
|
});
|
|
@@ -118,7 +118,13 @@ describe("InboundTranslationSlice mutation — response envelope", () => {
|
|
|
118
118
|
|
|
119
119
|
InboundTranslationResolvers_GraphQL.bindReceive(
|
|
120
120
|
~fieldName,
|
|
121
|
-
~receive=(inputJson, ~caller=?) =>
|
|
121
|
+
~receive=(inputJson, ~caller=?) =>
|
|
122
|
+
Callback.receive(
|
|
123
|
+
publishJsons,
|
|
124
|
+
inputJson,
|
|
125
|
+
~capabilities=Reventless.Capabilities.none,
|
|
126
|
+
~caller?,
|
|
127
|
+
),
|
|
122
128
|
)
|
|
123
129
|
})
|
|
124
130
|
|
|
@@ -221,7 +227,12 @@ describe("InboundTranslationSlice mutation — the door", () => {
|
|
|
221
227
|
InboundTranslationResolvers_GraphQL.bindReceive(
|
|
222
228
|
~fieldName=feedField,
|
|
223
229
|
~receive=(inputJson, ~caller=?) =>
|
|
224
|
-
CatalogFeedCallback.receive(
|
|
230
|
+
CatalogFeedCallback.receive(
|
|
231
|
+
publishJsons,
|
|
232
|
+
inputJson,
|
|
233
|
+
~capabilities=Reventless.Capabilities.none,
|
|
234
|
+
~caller?,
|
|
235
|
+
),
|
|
225
236
|
)
|
|
226
237
|
})
|
|
227
238
|
|
|
@@ -8,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
8
8
|
import * as Primitive_string from "@rescript/runtime/lib/es6/Primitive_string.js";
|
|
9
9
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
10
10
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
11
|
+
import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
|
|
11
12
|
import * as Dcb_Builder$ReventlessCore from "@reventlessdev/reventless-core/src/components/Dcb/Dcb_Builder.res.mjs";
|
|
12
13
|
import * as DomainGraphQL_Server$ReventlessLocal from "../../src/adapter/DomainGraphQL_Server.res.mjs";
|
|
13
14
|
import * as InboundTranslationSliceFixtures$ReventlessLocal from "../components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs";
|
|
@@ -31,7 +32,8 @@ let Callback = InboundTranslationSlice_Callback$ReventlessCore.Make({
|
|
|
31
32
|
targetName: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.targetName,
|
|
32
33
|
externalSystem: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.externalSystem,
|
|
33
34
|
authorizationOf: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.authorizationOf,
|
|
34
|
-
endpoint: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.endpoint
|
|
35
|
+
endpoint: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.endpoint,
|
|
36
|
+
capabilityNeeds: InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.capabilityNeeds
|
|
35
37
|
})(PaymentWebhookTranslation);
|
|
36
38
|
|
|
37
39
|
let scope = "Payments";
|
|
@@ -113,7 +115,7 @@ globalThis.describe("InboundTranslationSlice mutation — response envelope", ()
|
|
|
113
115
|
DomainGraphQL_Server$ReventlessLocal.setScope(scope);
|
|
114
116
|
InboundTranslationResolvers_GraphQL$ReventlessLocal.register(fieldName, InboundTranslationSliceFixtures$ReventlessLocal.PaymentWebhookSpec.externalInputSchema, "AllowAuthenticated", DomainGraphQL_Server$ReventlessLocal.asInterface);
|
|
115
117
|
DomainGraphQL_Server$ReventlessLocal.resetScope();
|
|
116
|
-
InboundTranslationResolvers_GraphQL$ReventlessLocal.bindReceive(fieldName, (inputJson, caller) => Callback.receive(publishJsons, inputJson, caller, undefined));
|
|
118
|
+
InboundTranslationResolvers_GraphQL$ReventlessLocal.bindReceive(fieldName, (inputJson, caller) => Callback.receive(publishJsons, inputJson, Capabilities$Reventless.none, caller, undefined));
|
|
117
119
|
});
|
|
118
120
|
globalThis.test("a successful translation resolves as CommandAccepted, with no errors", async () => {
|
|
119
121
|
let result = await runMutation("completed", undefined);
|
|
@@ -181,7 +183,8 @@ let CatalogFeedCallback = InboundTranslationSlice_Callback$ReventlessCore.Make({
|
|
|
181
183
|
targetName: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.targetName,
|
|
182
184
|
externalSystem: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.externalSystem,
|
|
183
185
|
authorizationOf: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.authorizationOf,
|
|
184
|
-
endpoint: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.endpoint
|
|
186
|
+
endpoint: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.endpoint,
|
|
187
|
+
capabilityNeeds: InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.capabilityNeeds
|
|
185
188
|
})(CatalogFeedTranslation);
|
|
186
189
|
|
|
187
190
|
let feedField = "Catalog_CatalogFeed";
|
|
@@ -197,7 +200,7 @@ globalThis.describe("InboundTranslationSlice mutation — the door", () => {
|
|
|
197
200
|
DomainGraphQL_Server$ReventlessLocal.setScope("Catalog");
|
|
198
201
|
InboundTranslationResolvers_GraphQL$ReventlessLocal.register(feedField, InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.externalInputSchema, Dcb_Builder$ReventlessCore.inboundDoorPermission(InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.commandSchema, InboundTranslationSliceFixtures$ReventlessLocal.CatalogFeedSpec.authorizationOf), DomainGraphQL_Server$ReventlessLocal.asInterface);
|
|
199
202
|
DomainGraphQL_Server$ReventlessLocal.resetScope();
|
|
200
|
-
InboundTranslationResolvers_GraphQL$ReventlessLocal.bindReceive(feedField, (inputJson, caller) => CatalogFeedCallback.receive(publishJsons, inputJson, caller, undefined));
|
|
203
|
+
InboundTranslationResolvers_GraphQL$ReventlessLocal.bindReceive(feedField, (inputJson, caller) => CatalogFeedCallback.receive(publishJsons, inputJson, Capabilities$Reventless.none, caller, undefined));
|
|
201
204
|
});
|
|
202
205
|
globalThis.test("a caller holding none of the commands' roles is refused", async () => {
|
|
203
206
|
let node = payload(await runFeed(signedIn(["Ops"]), "product"));
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
open JestGlobals
|
|
2
|
+
|
|
3
|
+
// The local doors' limit, as the resolvers apply it: only a rule that admits
|
|
4
|
+
// anonymous callers is counted, and the answer is a `Throttled` rejection.
|
|
5
|
+
|
|
6
|
+
@set_index external setEnv: (dict<string>, string, string) => unit = ""
|
|
7
|
+
|
|
8
|
+
let ctxFrom = (address: string): JSON.t =>
|
|
9
|
+
JSON.Encode.object(Dict.fromArray([("sourceAddress", JSON.Encode.string(address))]))
|
|
10
|
+
|
|
11
|
+
let throttled = (~rule, ~ctx) =>
|
|
12
|
+
CommandGeneratorResolvers_GraphQL.throttled(~rule, ~field="Registration_SignUp", ~ctx)
|
|
13
|
+
|
|
14
|
+
describe("the limit on anonymous doors", () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
NodeProcess.env->setEnv(Reventless.AnonymousRequestLimit.envKey, "2/3600")
|
|
17
|
+
LocalAnonymousLimit.reset()
|
|
18
|
+
})
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
NodeProcess.env->Dict.delete(Reventless.AnonymousRequestLimit.envKey)
|
|
21
|
+
LocalAnonymousLimit.reset()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
testSync("an anonymous door is limited per source address", () => {
|
|
25
|
+
let ctx = ctxFrom("10.0.0.1")
|
|
26
|
+
let answers = [
|
|
27
|
+
throttled(~rule=AllowAnonymous, ~ctx)->Option.isSome,
|
|
28
|
+
throttled(~rule=AllowAnonymous, ~ctx)->Option.isSome,
|
|
29
|
+
throttled(~rule=AllowAnonymous, ~ctx)->Option.isSome,
|
|
30
|
+
]
|
|
31
|
+
expect(answers)->toEqual([false, false, true])
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
testSync("another source is not held back by the first", () => {
|
|
35
|
+
let _ = throttled(~rule=AllowAnonymous, ~ctx=ctxFrom("10.0.0.1"))
|
|
36
|
+
let _ = throttled(~rule=AllowAnonymous, ~ctx=ctxFrom("10.0.0.1"))
|
|
37
|
+
expect(throttled(~rule=AllowAnonymous, ~ctx=ctxFrom("10.0.0.2"))->Option.isSome)->toBe(false)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
testSync("a door for signed-in callers is never counted", () => {
|
|
41
|
+
let ctx = ctxFrom("10.0.0.1")
|
|
42
|
+
let answers = Array.fromInitializer(
|
|
43
|
+
~length=5,
|
|
44
|
+
_ => throttled(~rule=AllowAuthenticated, ~ctx)->Option.isSome,
|
|
45
|
+
)
|
|
46
|
+
expect(answers->Array.some(x => x))->toBe(false)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
testSync("the refusal is a Throttled rejection, retryable by its cause", () => {
|
|
50
|
+
let ctx = ctxFrom("10.0.0.1")
|
|
51
|
+
let _ = throttled(~rule=AllowAnonymous, ~ctx)
|
|
52
|
+
let _ = throttled(~rule=AllowAnonymous, ~ctx)
|
|
53
|
+
let rejection = throttled(~rule=AllowAnonymous, ~ctx)->Option.getOr(JSON.Encode.null)
|
|
54
|
+
let text = rejection->JSON.stringify
|
|
55
|
+
expect((text->String.includes("Throttled"), text->String.includes("INFRASTRUCTURE")))->toEqual((
|
|
56
|
+
true,
|
|
57
|
+
true,
|
|
58
|
+
))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
testSync("a mistyped limit fails rather than becoming the default", () => {
|
|
62
|
+
NodeProcess.env->setEnv(Reventless.AnonymousRequestLimit.envKey, "ten")
|
|
63
|
+
LocalAnonymousLimit.reset()
|
|
64
|
+
let threw = switch throttled(~rule=AllowAnonymous, ~ctx=ctxFrom("10.0.0.1")) {
|
|
65
|
+
| _ => false
|
|
66
|
+
| exception _ => true
|
|
67
|
+
}
|
|
68
|
+
expect(threw)->toBe(true)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
4
|
+
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
+
import * as AnonymousRequestLimit$Reventless from "@reventlessdev/reventless-spec/src/types/AnonymousRequestLimit.res.mjs";
|
|
7
|
+
import * as LocalAnonymousLimit$ReventlessLocal from "../../src/adapter/LocalAnonymousLimit.res.mjs";
|
|
8
|
+
import * as CommandGeneratorResolvers_GraphQL$ReventlessLocal from "../../src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs";
|
|
9
|
+
|
|
10
|
+
function ctxFrom(address) {
|
|
11
|
+
return Object.fromEntries([[
|
|
12
|
+
"sourceAddress",
|
|
13
|
+
address
|
|
14
|
+
]]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function throttled(rule, ctx) {
|
|
18
|
+
return CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled(rule, "Registration_SignUp", ctx);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
globalThis.describe("the limit on anonymous doors", () => {
|
|
22
|
+
globalThis.beforeEach(() => {
|
|
23
|
+
process.env[AnonymousRequestLimit$Reventless.envKey] = "2/3600";
|
|
24
|
+
LocalAnonymousLimit$ReventlessLocal.reset();
|
|
25
|
+
});
|
|
26
|
+
globalThis.afterEach(() => {
|
|
27
|
+
Stdlib_Dict.$$delete(process.env, AnonymousRequestLimit$Reventless.envKey);
|
|
28
|
+
LocalAnonymousLimit$ReventlessLocal.reset();
|
|
29
|
+
});
|
|
30
|
+
globalThis.test("an anonymous door is limited per source address", () => {
|
|
31
|
+
let ctx = ctxFrom("10.0.0.1");
|
|
32
|
+
let answers = [
|
|
33
|
+
Stdlib_Option.isSome(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx)),
|
|
34
|
+
Stdlib_Option.isSome(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx)),
|
|
35
|
+
Stdlib_Option.isSome(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx))
|
|
36
|
+
];
|
|
37
|
+
globalThis.expect(answers).toEqual([
|
|
38
|
+
false,
|
|
39
|
+
false,
|
|
40
|
+
true
|
|
41
|
+
]);
|
|
42
|
+
});
|
|
43
|
+
globalThis.test("another source is not held back by the first", () => {
|
|
44
|
+
CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctxFrom("10.0.0.1"));
|
|
45
|
+
CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctxFrom("10.0.0.1"));
|
|
46
|
+
globalThis.expect(Stdlib_Option.isSome(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctxFrom("10.0.0.2")))).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
globalThis.test("a door for signed-in callers is never counted", () => {
|
|
49
|
+
let ctx = ctxFrom("10.0.0.1");
|
|
50
|
+
let answers = Stdlib_Array.fromInitializer(5, param => Stdlib_Option.isSome(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAuthenticated", "Registration_SignUp", ctx)));
|
|
51
|
+
globalThis.expect(answers.some(x => x)).toBe(false);
|
|
52
|
+
});
|
|
53
|
+
globalThis.test("the refusal is a Throttled rejection, retryable by its cause", () => {
|
|
54
|
+
let ctx = ctxFrom("10.0.0.1");
|
|
55
|
+
CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx);
|
|
56
|
+
CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx);
|
|
57
|
+
let rejection = Stdlib_Option.getOr(CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctx), null);
|
|
58
|
+
let text = JSON.stringify(rejection);
|
|
59
|
+
globalThis.expect([
|
|
60
|
+
text.includes("Throttled"),
|
|
61
|
+
text.includes("INFRASTRUCTURE")
|
|
62
|
+
]).toEqual([
|
|
63
|
+
true,
|
|
64
|
+
true
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
globalThis.test("a mistyped limit fails rather than becoming the default", () => {
|
|
68
|
+
process.env[AnonymousRequestLimit$Reventless.envKey] = "ten";
|
|
69
|
+
LocalAnonymousLimit$ReventlessLocal.reset();
|
|
70
|
+
let threw;
|
|
71
|
+
try {
|
|
72
|
+
CommandGeneratorResolvers_GraphQL$ReventlessLocal.throttled("AllowAnonymous", "Registration_SignUp", ctxFrom("10.0.0.1"));
|
|
73
|
+
threw = false;
|
|
74
|
+
} catch (exn) {
|
|
75
|
+
threw = true;
|
|
76
|
+
}
|
|
77
|
+
globalThis.expect(threw).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
ctxFrom,
|
|
83
|
+
throttled,
|
|
84
|
+
}
|
|
85
|
+
/* Not a pure module */
|
|
@@ -394,8 +394,8 @@ describe("the conformance table, minted locally", () => {
|
|
|
394
394
|
ReventlessCore.Auth_ActiveRole.conformanceCases->Array.forEach(({
|
|
395
395
|
label,
|
|
396
396
|
membership,
|
|
397
|
-
requested,
|
|
398
|
-
expected,
|
|
397
|
+
?requested,
|
|
398
|
+
?expected,
|
|
399
399
|
}) =>
|
|
400
400
|
testPromise(
|
|
401
401
|
label,
|