@reventlessdev/reventless-aws 3.0.0-alpha.188 → 3.0.0-alpha.190

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.
@@ -0,0 +1,18 @@
1
+ // Behavior pair for `AggTestAggregate`. Create-once / reject-on-seen.
2
+
3
+ type state = NotCreated | Active
4
+
5
+ let initialState = NotCreated
6
+
7
+ let evolve = (_state, _event: AggTestAggregate.event) => Active
8
+
9
+ let decide = (state, command: AggTestAggregate.command): result<
10
+ array<AggTestAggregate.event>,
11
+ AggTestAggregate.error,
12
+ > =>
13
+ switch (state, command) {
14
+ | (Active, _) => Error(AlreadyExists)
15
+ | (NotCreated, Add({name})) => Ok([Added({name: name})])
16
+ }
17
+
18
+ let moduleUrl = "agg-test://AggTestAggregateBehavior"
@@ -0,0 +1,35 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ function evolve(_state, _event) {
5
+ return "Active";
6
+ }
7
+
8
+ function decide(state, command) {
9
+ if (state === "NotCreated") {
10
+ return {
11
+ TAG: "Ok",
12
+ _0: [{
13
+ TAG: "Added",
14
+ name: command.name
15
+ }]
16
+ };
17
+ } else {
18
+ return {
19
+ TAG: "Error",
20
+ _0: "AlreadyExists"
21
+ };
22
+ }
23
+ }
24
+
25
+ let initialState = "NotCreated";
26
+
27
+ let moduleUrl = "agg-test://AggTestAggregateBehavior";
28
+
29
+ export {
30
+ initialState,
31
+ evolve,
32
+ decide,
33
+ moduleUrl,
34
+ }
35
+ /* No side effect */
@@ -0,0 +1,103 @@
1
+ // Integration regression guard for the Aggregate Lambda entry point.
2
+ //
3
+ // Drives the real `AggregateEntryPoint.mjs` against a DynamoDB Local EventLog
4
+ // table, with a test loader supplying a tiny `AggTestAggregate` spec/behavior
5
+ // pair in place of the production Lambda's `/var/task/node_modules` dynamic
6
+ // import. Covers the same regression class as the DCB entry-point test: the
7
+ // shell wires the compiled EventLog/Aggregate/CommandTopic functors and the
8
+ // CommandGenerator by structural contract, so any drift between the JS caller
9
+ // and the ReScript-compiled shapes (a renamed callback field, a shifted arg)
10
+ // re-breaks the AppSync command path. This test fails it as
11
+ // CommandAccepted -> Lambda:Unhandled.
12
+ //
13
+ // This is also the harness that unblocks moving the Aggregate functor wiring
14
+ // behind a typed core (docs/plans/minimize-lambda-entrypoint-mjs-shell.md,
15
+ // Tier 2.5): with it green, that refactor is verifiable end-to-end rather than
16
+ // only at compile time. Boots via the same Docker-gated
17
+ // `pnpm run test:integration` suite as the storage-runtime tests.
18
+
19
+ open JestGlobals
20
+
21
+ module H = AggIntegrationHarness
22
+
23
+ // Build handlers via the entry point's exported factory, dispatch one AppSync
24
+ // direct-invoke event, run the returned Effect through the request context the
25
+ // production handler supplies, and return the AppSync-shape outcome JSON.
26
+ let runOneAppSyncEvent: (string, JSON.t) => promise<JSON.t> = %raw(`
27
+ async (tableName, event) => {
28
+ const { buildHandlersForConfig } = await import(
29
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
30
+ );
31
+ const Effect = await import("effect/Effect");
32
+ const { tag: requestContextTag } = await import(
33
+ "@reventlessdev/reventless-core/src/RequestContext.res.mjs"
34
+ );
35
+ const { commandOutcomeToJson } = await import(
36
+ "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs"
37
+ );
38
+
39
+ const loadModule = async (specifier) => {
40
+ if (specifier === "agg-test://spec") return await import("./AggTestAggregate.res.mjs");
41
+ if (specifier === "agg-test://behavior") return await import("./AggTestAggregateBehavior.res.mjs");
42
+ throw new Error("unknown test specifier: " + specifier);
43
+ };
44
+
45
+ const config = {
46
+ handlers: [{
47
+ specModule: "agg-test://spec",
48
+ behaviorModule: "agg-test://behavior",
49
+ eventLogTable: tableName,
50
+ queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-test-queue",
51
+ queueArn: "arn:aws:sqs:eu-west-1:000000000000:agg-test-queue",
52
+ }],
53
+ };
54
+
55
+ const [, cmdGenHandlers] = await buildHandlersForConfig(config, { loadModule });
56
+ const cmdGenHandler = cmdGenHandlers["AggTestAggregate"];
57
+
58
+ const effect = cmdGenHandler(event, {})
59
+ .pipe(Effect.provideService(requestContextTag, { correlationId: "agg-test" }));
60
+ const outcome = await Effect.runPromise(effect);
61
+ return commandOutcomeToJson(outcome);
62
+ }
63
+ `)
64
+
65
+ // CommandGenerator payload: `{command, arguments, meta}` — the shape AppSync's
66
+ // direct-invoke resolver sends. `command` is the constructor name as a string;
67
+ // `arguments.id` is the aggregate/EventLog id (stripped from the command params
68
+ // by makeGenerateCommand's stripIdFromParams).
69
+ let buildAppSyncEvent = (~id, ~name): JSON.t => {
70
+ let arguments = Dict.fromArray([
71
+ ("id", id->JSON.Encode.string),
72
+ ("name", name->JSON.Encode.string),
73
+ ])
74
+ let meta = Dict.fromArray([
75
+ ("user", "agg-test"->JSON.Encode.string),
76
+ ("ip", JSON.Encode.null),
77
+ ])
78
+ Dict.fromArray([
79
+ ("command", "Add"->JSON.Encode.string),
80
+ ("arguments", arguments->JSON.Encode.object),
81
+ ("meta", meta->JSON.Encode.object),
82
+ ])->JSON.Encode.object
83
+ }
84
+
85
+ describe("AggregateEntryPoint integration", () => {
86
+ testAsync(
87
+ "AppSync direct-invoke routes through the runtime handler to CommandAccepted",
88
+ async () => {
89
+ // Own table-name prefix avoids collisions when sharing a DDB Local
90
+ // instance with sibling suites.
91
+ let table = await H.createEventLogTable("AggIt_" ++ Date.now()->Float.toString)
92
+ let event = buildAppSyncEvent(~id="agg-1", ~name="Widget One")
93
+ let outcomeJson = await runOneAppSyncEvent(table.name, event)
94
+
95
+ // A functor/arg-shape drift manifests as a thrown error from `runPromise`
96
+ // before `commandOutcomeToJson` runs, so reaching this assertion already
97
+ // proves the regression hasn't returned; we still assert CommandAccepted
98
+ // so a drift to silent rejection also fails.
99
+ let s = outcomeJson->JSON.stringifyAny->Option.getOr("<unserializable>")
100
+ expect(s->String.includes("CommandAccepted"))->toBe(true)
101
+ },
102
+ )
103
+ })
@@ -0,0 +1,97 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as AggIntegrationHarness$ReventlessAws from "./AggIntegrationHarness.res.mjs";
5
+
6
+ let runOneAppSyncEvent = (async (tableName, event) => {
7
+ const { buildHandlersForConfig } = await import(
8
+ "@reventlessdev/reventless-aws/src/adapter/Runtime/AggregateEntryPoint.mjs"
9
+ );
10
+ const Effect = await import("effect/Effect");
11
+ const { tag: requestContextTag } = await import(
12
+ "@reventlessdev/reventless-core/src/RequestContext.res.mjs"
13
+ );
14
+ const { commandOutcomeToJson } = await import(
15
+ "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs"
16
+ );
17
+
18
+ const loadModule = async (specifier) => {
19
+ if (specifier === "agg-test://spec") return await import("./AggTestAggregate.res.mjs");
20
+ if (specifier === "agg-test://behavior") return await import("./AggTestAggregateBehavior.res.mjs");
21
+ throw new Error("unknown test specifier: " + specifier);
22
+ };
23
+
24
+ const config = {
25
+ handlers: [{
26
+ specModule: "agg-test://spec",
27
+ behaviorModule: "agg-test://behavior",
28
+ eventLogTable: tableName,
29
+ queueUrl: "https://sqs.eu-west-1.amazonaws.com/000000000000/agg-test-queue",
30
+ queueArn: "arn:aws:sqs:eu-west-1:000000000000:agg-test-queue",
31
+ }],
32
+ };
33
+
34
+ const [, cmdGenHandlers] = await buildHandlersForConfig(config, { loadModule });
35
+ const cmdGenHandler = cmdGenHandlers["AggTestAggregate"];
36
+
37
+ const effect = cmdGenHandler(event, {})
38
+ .pipe(Effect.provideService(requestContextTag, { correlationId: "agg-test" }));
39
+ const outcome = await Effect.runPromise(effect);
40
+ return commandOutcomeToJson(outcome);
41
+ });
42
+
43
+ function buildAppSyncEvent(id, name) {
44
+ let $$arguments = Object.fromEntries([
45
+ [
46
+ "id",
47
+ id
48
+ ],
49
+ [
50
+ "name",
51
+ name
52
+ ]
53
+ ]);
54
+ let meta = Object.fromEntries([
55
+ [
56
+ "user",
57
+ "agg-test"
58
+ ],
59
+ [
60
+ "ip",
61
+ null
62
+ ]
63
+ ]);
64
+ return Object.fromEntries([
65
+ [
66
+ "command",
67
+ "Add"
68
+ ],
69
+ [
70
+ "arguments",
71
+ $$arguments
72
+ ],
73
+ [
74
+ "meta",
75
+ meta
76
+ ]
77
+ ]);
78
+ }
79
+
80
+ globalThis.describe("AggregateEntryPoint integration", () => {
81
+ globalThis.test("AppSync direct-invoke routes through the runtime handler to CommandAccepted", async () => {
82
+ let table = await AggIntegrationHarness$ReventlessAws.createEventLogTable("AggIt_" + Date.now().toString());
83
+ let event = buildAppSyncEvent("agg-1", "Widget One");
84
+ let outcomeJson = await runOneAppSyncEvent(table.name, event);
85
+ let s = Stdlib_Option.getOr(JSON.stringify(outcomeJson), "<unserializable>");
86
+ globalThis.expect(s.includes("CommandAccepted")).toBe(true);
87
+ });
88
+ });
89
+
90
+ let H;
91
+
92
+ export {
93
+ H,
94
+ runOneAppSyncEvent,
95
+ buildAppSyncEvent,
96
+ }
97
+ /* runOneAppSyncEvent Not a pure module */