@reventlessdev/reventless-local 3.0.0-alpha.113
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 +1197 -0
- package/LICENSE +202 -0
- package/__mocks__/emptyModule.js +2 -0
- package/__mocks__/nodeSqlite.mjs +6 -0
- package/package.json +67 -0
- package/rescript.json +39 -0
- package/src/Platform.res +2162 -0
- package/src/Platform.res.mjs +3389 -0
- package/src/adapter/Api/LocalGraphQL_Adapter.res +29 -0
- package/src/adapter/Api/LocalGraphQL_Adapter.res.mjs +26 -0
- package/src/adapter/Api/LocalGraphQL_SubscriptionResolvers.res +146 -0
- package/src/adapter/Api/LocalGraphQL_SubscriptionResolvers.res.mjs +88 -0
- package/src/adapter/Auth/Auth_GraphqlContext.res +83 -0
- package/src/adapter/Auth/Auth_GraphqlContext.res.mjs +76 -0
- package/src/adapter/Auth/LocalAuth.res +239 -0
- package/src/adapter/Auth/LocalAuth.res.mjs +247 -0
- package/src/adapter/Auth/UserStore.res +134 -0
- package/src/adapter/Auth/UserStore.res.mjs +146 -0
- package/src/adapter/Backend.res +46 -0
- package/src/adapter/Backend.res.mjs +66 -0
- package/src/adapter/BackendState.res +34 -0
- package/src/adapter/BackendState.res.mjs +42 -0
- package/src/adapter/Cloner/LocalClonerRunner.res +13 -0
- package/src/adapter/Cloner/LocalClonerRunner.res.mjs +12 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res +329 -0
- package/src/adapter/CommandGenerator/CommandGeneratorResolvers_GraphQL.res.mjs +301 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res +76 -0
- package/src/adapter/CommandGenerator/InboundTranslationResolvers_GraphQL.res.mjs +57 -0
- package/src/adapter/CommandGenerator/LocalCommandGeneratorResolvers.res +19 -0
- package/src/adapter/CommandGenerator/LocalCommandGeneratorResolvers.res.mjs +19 -0
- package/src/adapter/CommandTopic/LocalCommandTopicChannel.res +104 -0
- package/src/adapter/CommandTopic/LocalCommandTopicChannel.res.mjs +82 -0
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res +32 -0
- package/src/adapter/CommandTopic/LocalCommandTopicRemoteChannel.res.mjs +38 -0
- package/src/adapter/Counter/LocalCounterHandler.res +42 -0
- package/src/adapter/Counter/LocalCounterHandler.res.mjs +45 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +131 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +140 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +334 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +299 -0
- package/src/adapter/DomainGraphQL_Server.res +565 -0
- package/src/adapter/DomainGraphQL_Server.res.mjs +576 -0
- package/src/adapter/DomainMCP_Server.res +369 -0
- package/src/adapter/DomainMCP_Server.res.mjs +358 -0
- package/src/adapter/EventCollector/LocalEventCollectorChannel.res +87 -0
- package/src/adapter/EventCollector/LocalEventCollectorChannel.res.mjs +45 -0
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +105 -0
- package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +117 -0
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +145 -0
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +155 -0
- package/src/adapter/EventTopic/LocalEventTopicPublisher.res +36 -0
- package/src/adapter/EventTopic/LocalEventTopicPublisher.res.mjs +31 -0
- package/src/adapter/GraphQL_Server.res +6 -0
- package/src/adapter/GraphQL_Server.res.mjs +154 -0
- package/src/adapter/GraphQL_ServerInstance.res +310 -0
- package/src/adapter/GraphQL_ServerInstance.res.mjs +257 -0
- package/src/adapter/Heartbeat/LocalHeartbeatRunner.res +57 -0
- package/src/adapter/Heartbeat/LocalHeartbeatRunner.res.mjs +34 -0
- package/src/adapter/LocalBus.res +498 -0
- package/src/adapter/LocalBus.res.mjs +1011 -0
- package/src/adapter/LocalPluginSpec.res +16 -0
- package/src/adapter/LocalPluginSpec.res.mjs +33 -0
- package/src/adapter/LocalSideEffectHandler.res +44 -0
- package/src/adapter/LocalSideEffectHandler.res.mjs +34 -0
- package/src/adapter/MCP_Server.res +4 -0
- package/src/adapter/MCP_Server.res.mjs +67 -0
- package/src/adapter/MCP_ServerInstance.res +362 -0
- package/src/adapter/MCP_ServerInstance.res.mjs +313 -0
- package/src/adapter/PlatformGraphQL_Server.res +75 -0
- package/src/adapter/PlatformGraphQL_Server.res.mjs +84 -0
- package/src/adapter/PlatformMCP_Server.res +23 -0
- package/src/adapter/PlatformMCP_Server.res.mjs +46 -0
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res +793 -0
- package/src/adapter/QueryDb/QueryDbResolvers_GraphQL.res.mjs +569 -0
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res +242 -0
- package/src/adapter/QueryDb/QueryDbStorage_InMemory.res.mjs +241 -0
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res +339 -0
- package/src/adapter/QueryDb/QueryDbStorage_Sqlite.res.mjs +318 -0
- package/src/adapter/QueryDb/SortKey_Filter.res +48 -0
- package/src/adapter/QueryDb/SortKey_Filter.res.mjs +46 -0
- package/src/adapter/QueryEngine/LocalQueryEngine.res +57 -0
- package/src/adapter/QueryEngine/LocalQueryEngine.res.mjs +56 -0
- package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res +91 -0
- package/src/adapter/Runtime/LocalAggregateRuntime_Builder.res.mjs +50 -0
- package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res +47 -0
- package/src/adapter/Runtime/LocalEventCollectorRuntime_Builder.res.mjs +37 -0
- package/src/adapter/Runtime/LocalPluginRuntime_Builder.res +11 -0
- package/src/adapter/Runtime/LocalPluginRuntime_Builder.res.mjs +30 -0
- package/src/adapter/Runtime/LocalRuntimeEnvironment.res +54 -0
- package/src/adapter/Runtime/LocalRuntimeEnvironment.res.mjs +40 -0
- package/src/adapter/Scheduler/LocalScheduledPublisher.res +102 -0
- package/src/adapter/Scheduler/LocalScheduledPublisher.res.mjs +84 -0
- package/src/adapter/SqliteDriver.res +52 -0
- package/src/adapter/SqliteDriver.res.mjs +76 -0
- package/src/adapter/SqliteDriver.resi +31 -0
- package/src/adapter/Task/LocalTaskBucket.res +75 -0
- package/src/adapter/Task/LocalTaskBucket.res.mjs +48 -0
- package/src/adapter/Task/TaskBucket_Sqlite.res +47 -0
- package/src/adapter/Task/TaskBucket_Sqlite.res.mjs +40 -0
- package/src/components/Aggregate_Builder.res +55 -0
- package/src/components/Aggregate_Builder.res.mjs +140 -0
- package/src/components/AutomationSlice_Builder.res +41 -0
- package/src/components/AutomationSlice_Builder.res.mjs +57 -0
- package/src/components/Counter_Builder.res +14 -0
- package/src/components/Counter_Builder.res.mjs +32 -0
- package/src/components/DcbEventLog_Builder.res +18 -0
- package/src/components/DcbEventLog_Builder.res.mjs +24 -0
- package/src/components/ExtensionPoint_Builder.res +25 -0
- package/src/components/ExtensionPoint_Builder.res.mjs +44 -0
- package/src/components/InboundTranslationSlice_Builder.res +34 -0
- package/src/components/InboundTranslationSlice_Builder.res.mjs +45 -0
- package/src/components/OutboundTranslationSlice_Builder.res +43 -0
- package/src/components/OutboundTranslationSlice_Builder.res.mjs +59 -0
- package/src/components/Plugin_Builder.res +37 -0
- package/src/components/Plugin_Builder.res.mjs +67 -0
- package/src/components/ReadModel_Builder.res +47 -0
- package/src/components/ReadModel_Builder.res.mjs +54 -0
- package/src/components/StateChangeSlice_Builder.res +19 -0
- package/src/components/StateChangeSlice_Builder.res.mjs +21 -0
- package/src/components/StateViewSlice_Builder.res +42 -0
- package/src/components/StateViewSlice_Builder.res.mjs +58 -0
- package/src/components/Task_Builder.res +28 -0
- package/src/components/Task_Builder.res.mjs +41 -0
- package/src/reexport/CommandTopic.res +3 -0
- package/src/reexport/CommandTopic.res.mjs +55 -0
- package/src/test/Mocks/MockDcbEventLogStorage.res +136 -0
- package/src/test/Mocks/MockDcbEventLogStorage.res.mjs +146 -0
- package/src/test/Mocks/MockEventLogStorage.res +57 -0
- package/src/test/Mocks/MockEventLogStorage.res.mjs +65 -0
- package/src/test/Mocks/MockPublisher.res +29 -0
- package/src/test/Mocks/MockPublisher.res.mjs +28 -0
- package/src/test/Mocks/MockQueryDbStorage.res +104 -0
- package/src/test/Mocks/MockQueryDbStorage.res.mjs +136 -0
- package/src/test/TestRunner.res +96 -0
- package/src/test/TestRunner.res.mjs +71 -0
- package/tests/SplitApiFixtures.res +84 -0
- package/tests/SplitApiFixtures.res.mjs +88 -0
- package/tests/SplitApiTest.res +102 -0
- package/tests/SplitApiTest.res.mjs +82 -0
- package/tests/TestFixtures.res +54 -0
- package/tests/TestFixtures.res.mjs +65 -0
- package/tests/adapter/BackendParityTest.res +94 -0
- package/tests/adapter/BackendParityTest.res.mjs +96 -0
- package/tests/adapter/CollectNEventsTest.res +82 -0
- package/tests/adapter/CollectNEventsTest.res.mjs +83 -0
- package/tests/adapter/CommandAuthorizationTest.res +200 -0
- package/tests/adapter/CommandAuthorizationTest.res.mjs +190 -0
- package/tests/adapter/CommandTopicChannelTest.res +174 -0
- package/tests/adapter/CommandTopicChannelTest.res.mjs +170 -0
- package/tests/adapter/CounterHandlerTest.res +113 -0
- package/tests/adapter/CounterHandlerTest.res.mjs +96 -0
- package/tests/adapter/DcbEventLogStorageSqliteTest.res +157 -0
- package/tests/adapter/DcbEventLogStorageSqliteTest.res.mjs +165 -0
- package/tests/adapter/DcbEventLogStorageTest.res +187 -0
- package/tests/adapter/DcbEventLogStorageTest.res.mjs +195 -0
- package/tests/adapter/EventCollectorChannelTest.res +166 -0
- package/tests/adapter/EventCollectorChannelTest.res.mjs +178 -0
- package/tests/adapter/EventLogStorageSqliteTest.res +105 -0
- package/tests/adapter/EventLogStorageSqliteTest.res.mjs +116 -0
- package/tests/adapter/EventTapTest.res +102 -0
- package/tests/adapter/EventTapTest.res.mjs +94 -0
- package/tests/adapter/EventTopicPublisherTest.res +76 -0
- package/tests/adapter/EventTopicPublisherTest.res.mjs +86 -0
- package/tests/adapter/GraphQL_SchemaInspectorTest.res +763 -0
- package/tests/adapter/GraphQL_SchemaInspectorTest.res.mjs +578 -0
- package/tests/adapter/GraphQL_SubscriptionResolversTest.res +161 -0
- package/tests/adapter/GraphQL_SubscriptionResolversTest.res.mjs +134 -0
- package/tests/adapter/HeartbeatRunnerTest.res +136 -0
- package/tests/adapter/HeartbeatRunnerTest.res.mjs +96 -0
- package/tests/adapter/LocalAuthHttpTest.res +224 -0
- package/tests/adapter/LocalAuthHttpTest.res.mjs +237 -0
- package/tests/adapter/LocalAuthLoginTest.res +180 -0
- package/tests/adapter/LocalAuthLoginTest.res.mjs +207 -0
- package/tests/adapter/LocalAuthTest.res +106 -0
- package/tests/adapter/LocalAuthTest.res.mjs +145 -0
- package/tests/adapter/LocalAuthUserStoreTest.res +172 -0
- package/tests/adapter/LocalAuthUserStoreTest.res.mjs +208 -0
- package/tests/adapter/LocalBusBoundedTest.res +157 -0
- package/tests/adapter/LocalBusBoundedTest.res.mjs +168 -0
- package/tests/adapter/LocalBusPubSubTest.res +81 -0
- package/tests/adapter/LocalBusPubSubTest.res.mjs +81 -0
- package/tests/adapter/QueryDbGsiTtlTest.res +268 -0
- package/tests/adapter/QueryDbGsiTtlTest.res.mjs +209 -0
- package/tests/adapter/QueryDbListResolverTest.res +361 -0
- package/tests/adapter/QueryDbListResolverTest.res.mjs +426 -0
- package/tests/adapter/QueryDbStorageSqliteTest.res +159 -0
- package/tests/adapter/QueryDbStorageSqliteTest.res.mjs +126 -0
- package/tests/adapter/QueryDbStorageTest.res +171 -0
- package/tests/adapter/QueryDbStorageTest.res.mjs +170 -0
- package/tests/adapter/QueryEngineTest.res +152 -0
- package/tests/adapter/QueryEngineTest.res.mjs +148 -0
- package/tests/adapter/ScheduledPublisherTest.res +199 -0
- package/tests/adapter/ScheduledPublisherTest.res.mjs +203 -0
- package/tests/adapter/SortKey_FilterTest.res +103 -0
- package/tests/adapter/SortKey_FilterTest.res.mjs +135 -0
- package/tests/adapter/SqliteDriverTest.res +86 -0
- package/tests/adapter/SqliteDriverTest.res.mjs +101 -0
- package/tests/adapter/TaskBucketSqliteTest.res +44 -0
- package/tests/adapter/TaskBucketSqliteTest.res.mjs +40 -0
- package/tests/adapter/TaskBucketTest.res +106 -0
- package/tests/adapter/TaskBucketTest.res.mjs +118 -0
- package/tests/components/aggregate/AggregateFixtures.res +78 -0
- package/tests/components/aggregate/AggregateFixtures.res.mjs +109 -0
- package/tests/components/aggregate/AggregateTest.res +129 -0
- package/tests/components/aggregate/AggregateTest.res.mjs +129 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res +212 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res.mjs +233 -0
- package/tests/components/automationslice/AutomationSliceFixtures.res +189 -0
- package/tests/components/automationslice/AutomationSliceFixtures.res.mjs +276 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +289 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +453 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res +47 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res.mjs +31 -0
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res +213 -0
- package/tests/components/automationslice/MixedSourceAutomationSliceFixtures.res.mjs +268 -0
- package/tests/components/automationslice/MixedSourceAutomationSliceTest.res +118 -0
- package/tests/components/automationslice/MixedSourceAutomationSliceTest.res.mjs +99 -0
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res +73 -0
- package/tests/components/commandgenerator/CommandGeneratorFixtures.res.mjs +109 -0
- package/tests/components/commandgenerator/CommandGeneratorTest.res +43 -0
- package/tests/components/commandgenerator/CommandGeneratorTest.res.mjs +46 -0
- package/tests/components/commandtopic/CommandTopicFixtures.res +37 -0
- package/tests/components/commandtopic/CommandTopicFixtures.res.mjs +68 -0
- package/tests/components/commandtopic/CommandTopicStreamFixtures.res +43 -0
- package/tests/components/commandtopic/CommandTopicStreamFixtures.res.mjs +91 -0
- package/tests/components/commandtopic/CommandTopicStreamTest.res +30 -0
- package/tests/components/commandtopic/CommandTopicStreamTest.res.mjs +35 -0
- package/tests/components/commandtopic/CommandTopicTest.res +55 -0
- package/tests/components/commandtopic/CommandTopicTest.res.mjs +62 -0
- package/tests/components/counter/CounterFixtures.res +42 -0
- package/tests/components/counter/CounterFixtures.res.mjs +34 -0
- package/tests/components/counter/CounterTest.res +46 -0
- package/tests/components/counter/CounterTest.res.mjs +61 -0
- package/tests/components/dcb/DcbCrossPartitionFixtures.res +170 -0
- package/tests/components/dcb/DcbCrossPartitionFixtures.res.mjs +276 -0
- package/tests/components/dcb/DcbCrossPartitionReadTest.res +68 -0
- package/tests/components/dcb/DcbCrossPartitionReadTest.res.mjs +78 -0
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res +72 -0
- package/tests/components/dcb/DcbEventLogAppendStreamTest.res.mjs +82 -0
- package/tests/components/dcb/DcbEventLogStreamTest.res +103 -0
- package/tests/components/dcb/DcbEventLogStreamTest.res.mjs +91 -0
- package/tests/components/dcb/DcbFixtures.res +145 -0
- package/tests/components/dcb/DcbFixtures.res.mjs +221 -0
- package/tests/components/dcb/DcbTest.res +55 -0
- package/tests/components/dcb/DcbTest.res.mjs +55 -0
- package/tests/components/eventcollector/EventCollectorFixtures.res +42 -0
- package/tests/components/eventcollector/EventCollectorFixtures.res.mjs +49 -0
- package/tests/components/eventcollector/EventCollectorTest.res +37 -0
- package/tests/components/eventcollector/EventCollectorTest.res.mjs +36 -0
- package/tests/components/eventlog/EventLogAppendStreamTest.res +93 -0
- package/tests/components/eventlog/EventLogAppendStreamTest.res.mjs +113 -0
- package/tests/components/eventlog/EventLogConflictTest.res +86 -0
- package/tests/components/eventlog/EventLogConflictTest.res.mjs +81 -0
- package/tests/components/eventlog/EventLogFixtures.res +52 -0
- package/tests/components/eventlog/EventLogFixtures.res.mjs +80 -0
- package/tests/components/eventlog/EventLogStreamTest.res +98 -0
- package/tests/components/eventlog/EventLogStreamTest.res.mjs +142 -0
- package/tests/components/eventlog/EventLogTest.res +53 -0
- package/tests/components/eventlog/EventLogTest.res.mjs +74 -0
- package/tests/components/eventtopic/EventTopicFixtures.res +47 -0
- package/tests/components/eventtopic/EventTopicFixtures.res.mjs +77 -0
- package/tests/components/eventtopic/EventTopicStreamFixtures.res +27 -0
- package/tests/components/eventtopic/EventTopicStreamFixtures.res.mjs +62 -0
- package/tests/components/eventtopic/EventTopicStreamTest.res +35 -0
- package/tests/components/eventtopic/EventTopicStreamTest.res.mjs +44 -0
- package/tests/components/eventtopic/EventTopicTest.res +35 -0
- package/tests/components/eventtopic/EventTopicTest.res.mjs +43 -0
- package/tests/components/extensionpoint/ExtensionPointFixtures.res +145 -0
- package/tests/components/extensionpoint/ExtensionPointFixtures.res.mjs +188 -0
- package/tests/components/extensionpoint/ExtensionPointTest.res +62 -0
- package/tests/components/extensionpoint/ExtensionPointTest.res.mjs +55 -0
- package/tests/components/heartbeat/HeartbeatFixtures.res +48 -0
- package/tests/components/heartbeat/HeartbeatFixtures.res.mjs +46 -0
- package/tests/components/heartbeat/HeartbeatTest.res +67 -0
- package/tests/components/heartbeat/HeartbeatTest.res.mjs +65 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res +241 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceCallbackTest.res.mjs +239 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res +36 -0
- package/tests/components/inboundtranslationslice/InboundTranslationSliceFixtures.res.mjs +73 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +191 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +297 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res +78 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceFixtures.res.mjs +137 -0
- package/tests/components/querydb/QueryDbFixtures.res +39 -0
- package/tests/components/querydb/QueryDbFixtures.res.mjs +73 -0
- package/tests/components/querydb/QueryDbSubIdFixtures.res +44 -0
- package/tests/components/querydb/QueryDbSubIdFixtures.res.mjs +80 -0
- package/tests/components/querydb/QueryDbSubIdTest.res +104 -0
- package/tests/components/querydb/QueryDbSubIdTest.res.mjs +104 -0
- package/tests/components/querydb/QueryDbTest.res +81 -0
- package/tests/components/querydb/QueryDbTest.res.mjs +126 -0
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res +215 -0
- package/tests/components/readmodel/DcbReadModelE2EFixtures.res.mjs +308 -0
- package/tests/components/readmodel/DcbReadModelE2ETest.res +45 -0
- package/tests/components/readmodel/DcbReadModelE2ETest.res.mjs +37 -0
- package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res +88 -0
- package/tests/components/readmodel/DcbReadModelTypoFailFastTest.res.mjs +152 -0
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res +181 -0
- package/tests/components/readmodel/MixedSourceReadModelFixtures.res.mjs +277 -0
- package/tests/components/readmodel/MixedSourceReadModelTest.res +61 -0
- package/tests/components/readmodel/MixedSourceReadModelTest.res.mjs +68 -0
- package/tests/components/readmodel/ReadModelFixtures.res +138 -0
- package/tests/components/readmodel/ReadModelFixtures.res.mjs +169 -0
- package/tests/components/readmodel/ReadModelTest.res +46 -0
- package/tests/components/readmodel/ReadModelTest.res.mjs +36 -0
- package/tests/components/scheduler/SchedulerFixtures.res +59 -0
- package/tests/components/scheduler/SchedulerFixtures.res.mjs +63 -0
- package/tests/components/scheduler/SchedulerTest.res +76 -0
- package/tests/components/scheduler/SchedulerTest.res.mjs +86 -0
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res +133 -0
- package/tests/components/sideeffecthandler/SideEffectHandlerFixtures.res.mjs +149 -0
- package/tests/components/sideeffecthandler/SideEffectHandlerTest.res +39 -0
- package/tests/components/sideeffecthandler/SideEffectHandlerTest.res.mjs +28 -0
- package/tests/components/stateviewslice/StateViewSliceFixtures.res +117 -0
- package/tests/components/stateviewslice/StateViewSliceFixtures.res.mjs +188 -0
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res +117 -0
- package/tests/components/stateviewslice/StateViewSliceSubIdFixtures.res.mjs +183 -0
- package/tests/components/stateviewslice/StateViewSliceSubIdTest.res +78 -0
- package/tests/components/stateviewslice/StateViewSliceSubIdTest.res.mjs +107 -0
- package/tests/components/stateviewslice/StateViewSliceTest.res +59 -0
- package/tests/components/stateviewslice/StateViewSliceTest.res.mjs +72 -0
- package/tests/components/task/TaskFixtures.res +87 -0
- package/tests/components/task/TaskFixtures.res.mjs +82 -0
- package/tests/components/task/TaskTest.res +98 -0
- package/tests/components/task/TaskTest.res.mjs +46 -0
- package/tests/plugin/PluginBehavior_GWT.res +146 -0
- package/tests/plugin/PluginBehavior_GWT.res.mjs +281 -0
- package/tests/plugin/Plugin_Fixtures.res +61 -0
- package/tests/plugin/Plugin_Fixtures.res.mjs +105 -0
- package/tests/plugin/PluginsProjection_GWT.res +95 -0
- package/tests/plugin/PluginsProjection_GWT.res.mjs +120 -0
- package/tests/plugin/UIFragmentRegistryProjection_GWT.res +72 -0
- package/tests/plugin/UIFragmentRegistryProjection_GWT.res.mjs +117 -0
- package/tests/setup/sqliteGlobal.cjs +12 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Component$ReventlessCore from "@reventlessdev/reventless-core/src/components/Component.res.mjs";
|
|
7
|
+
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
8
|
+
import * as QueryDbFixtures$ReventlessLocal from "./QueryDbFixtures.res.mjs";
|
|
9
|
+
|
|
10
|
+
globalThis.describe("QueryDb (in-memory)", () => {
|
|
11
|
+
globalThis.beforeAll(async () => {
|
|
12
|
+
await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
13
|
+
});
|
|
14
|
+
globalThis.test("loadStream returns empty for unknown id", async () => {
|
|
15
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
16
|
+
let result = await Effect.runPromise(Stream.runCollect(ops.loadStream("unknown-id")));
|
|
17
|
+
globalThis.expect(result).toEqual([]);
|
|
18
|
+
});
|
|
19
|
+
globalThis.test("save and loadStream round-trip", async () => {
|
|
20
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
21
|
+
await ops.save("item-1", {
|
|
22
|
+
name: "Widget",
|
|
23
|
+
count: 5
|
|
24
|
+
}, "Init", undefined);
|
|
25
|
+
let result = await Effect.runPromise(Stream.runCollect(ops.loadStream("item-1")));
|
|
26
|
+
if (result.length !== 1) {
|
|
27
|
+
globalThis.expect("Expected [state]").toEqual("but got different result");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let s = result[0];
|
|
31
|
+
globalThis.expect([
|
|
32
|
+
s.name,
|
|
33
|
+
s.count
|
|
34
|
+
]).toEqual([
|
|
35
|
+
"Widget",
|
|
36
|
+
5
|
|
37
|
+
]);
|
|
38
|
+
});
|
|
39
|
+
globalThis.test("save overwrites previous state", async () => {
|
|
40
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
41
|
+
await ops.save("item-2", {
|
|
42
|
+
name: "Old",
|
|
43
|
+
count: 1
|
|
44
|
+
}, "Init", undefined);
|
|
45
|
+
await ops.save("item-2", {
|
|
46
|
+
name: "New",
|
|
47
|
+
count: 2
|
|
48
|
+
}, "Overwrite", undefined);
|
|
49
|
+
let result = await Effect.runPromise(Stream.runCollect(ops.loadStream("item-2")));
|
|
50
|
+
if (result.length !== 1) {
|
|
51
|
+
globalThis.expect("Expected [state]").toEqual("but got different result");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
let s = result[0];
|
|
55
|
+
globalThis.expect(s.name).toBe("New");
|
|
56
|
+
});
|
|
57
|
+
globalThis.test("saveBatch saves multiple states", async () => {
|
|
58
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
59
|
+
let batch = [
|
|
60
|
+
[
|
|
61
|
+
"batch-1",
|
|
62
|
+
{
|
|
63
|
+
name: "Alpha",
|
|
64
|
+
count: 1
|
|
65
|
+
},
|
|
66
|
+
undefined
|
|
67
|
+
],
|
|
68
|
+
[
|
|
69
|
+
"batch-2",
|
|
70
|
+
{
|
|
71
|
+
name: "Beta",
|
|
72
|
+
count: 2
|
|
73
|
+
},
|
|
74
|
+
undefined
|
|
75
|
+
]
|
|
76
|
+
];
|
|
77
|
+
await ops.saveBatch(batch);
|
|
78
|
+
let r1 = await Effect.runPromise(Stream.runCollect(ops.loadStream("batch-1")));
|
|
79
|
+
let r2 = await Effect.runPromise(Stream.runCollect(ops.loadStream("batch-2")));
|
|
80
|
+
globalThis.expect([
|
|
81
|
+
r1.length !== 0,
|
|
82
|
+
r2.length !== 0
|
|
83
|
+
]).toEqual([
|
|
84
|
+
true,
|
|
85
|
+
true
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
globalThis.test("delete removes state", async () => {
|
|
89
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
90
|
+
await ops.save("item-del", {
|
|
91
|
+
name: "ToDelete",
|
|
92
|
+
count: 0
|
|
93
|
+
}, "Init", undefined);
|
|
94
|
+
await ops.delete("item-del", undefined);
|
|
95
|
+
let result = await Effect.runPromise(Stream.runCollect(ops.loadStream("item-del")));
|
|
96
|
+
globalThis.expect(result).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
globalThis.test("loadStream emits saved states", async () => {
|
|
99
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
100
|
+
await ops.save("stream-item", {
|
|
101
|
+
name: "Streamed",
|
|
102
|
+
count: 42
|
|
103
|
+
}, "Init", undefined);
|
|
104
|
+
let arr = await Effect.runPromise(Stream.runCollect(ops.loadStream("stream-item")));
|
|
105
|
+
globalThis.expect(arr.length).toBe(1);
|
|
106
|
+
let s = arr[0];
|
|
107
|
+
globalThis.expect([
|
|
108
|
+
s.name,
|
|
109
|
+
s.count
|
|
110
|
+
]).toEqual([
|
|
111
|
+
"Streamed",
|
|
112
|
+
42
|
|
113
|
+
]);
|
|
114
|
+
});
|
|
115
|
+
globalThis.test("loadStream returns empty for unknown id", async () => {
|
|
116
|
+
let ops = await TestRunner$ReventlessLocal.resolve(Component$ReventlessCore.operations(QueryDbFixtures$ReventlessLocal.queryDb));
|
|
117
|
+
let arr = await Effect.runPromise(Stream.runCollect(ops.loadStream("no-such-id")));
|
|
118
|
+
globalThis.expect(arr.length).toBe(0);
|
|
119
|
+
});
|
|
120
|
+
globalThis.test("registered in bus by component name", async () => {
|
|
121
|
+
let db = QueryDbFixtures$ReventlessLocal.Bus.getQueryDb("TestItemQueryDb");
|
|
122
|
+
globalThis.expect(Stdlib_Option.isSome(db)).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// E2E test fixtures for DcbEventLog → ReadModel projection.
|
|
2
|
+
// Wires a real DcbEventLog + StateChangeSlice + ReadModel and verifies that
|
|
3
|
+
// events appended to the DCB log flow through the EventTopic and update the
|
|
4
|
+
// ReadModel's QueryDb. Exercises the Phase 1.5 fix that aligns
|
|
5
|
+
// `meta.service` with the `allEventTopics` dict key, plus the Phase 3
|
|
6
|
+
// `Reventless.Projection.DcbSource` helper.
|
|
7
|
+
//
|
|
8
|
+
// Plan 03: docs/plans/mixed-source-readmodel.md
|
|
9
|
+
|
|
10
|
+
open TestFixtures
|
|
11
|
+
open Reventless
|
|
12
|
+
open Reventless.Projection
|
|
13
|
+
|
|
14
|
+
// ─────────────────────────────────────────────────────────────
|
|
15
|
+
// DCB StateChangeSlice — emits events into a DCB EventLog
|
|
16
|
+
// ─────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
module AddProductSpec = {
|
|
19
|
+
let name = "AddProduct"
|
|
20
|
+
module Id = Reventless.Id.String
|
|
21
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
22
|
+
|
|
23
|
+
@schema
|
|
24
|
+
type event =
|
|
25
|
+
ProductAdded({
|
|
26
|
+
productId: @s.matches(Reventless.DcbTag.string) string,
|
|
27
|
+
name: string,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
@schema
|
|
31
|
+
type consumedEvent = ProductAdded
|
|
32
|
+
|
|
33
|
+
@schema
|
|
34
|
+
type command = AddProduct({
|
|
35
|
+
productId: @s.matches(Reventless.DcbTag.string) string,
|
|
36
|
+
name: string,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
@schema
|
|
40
|
+
type error = ProductAlreadyExists
|
|
41
|
+
|
|
42
|
+
let commandSchema = commandSchema
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
module AddProductBehavior = {
|
|
46
|
+
module Spec = AddProductSpec
|
|
47
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
48
|
+
|
|
49
|
+
type state = bool
|
|
50
|
+
let initialState = false
|
|
51
|
+
|
|
52
|
+
let evolve = (_state: state, _event: Spec.consumedEvent) => true
|
|
53
|
+
|
|
54
|
+
let decide = (state: state, command: Spec.command): result<array<Spec.event>, Spec.error> =>
|
|
55
|
+
if state {
|
|
56
|
+
Error(ProductAlreadyExists)
|
|
57
|
+
} else {
|
|
58
|
+
switch command {
|
|
59
|
+
| AddProduct({productId, name}) => Ok([Spec.ProductAdded({productId, name})])
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ─────────────────────────────────────────────────────────────
|
|
65
|
+
// DCB Source — name MUST match `<dcbName>DcbEventLog`
|
|
66
|
+
// (= the dict key Plugin_Builder uses + the service stamped by
|
|
67
|
+
// DcbEventLog_Operations after Phase 1.5).
|
|
68
|
+
//
|
|
69
|
+
// Hand-rolled `Source`-shaped module: this is the canonical pattern. The
|
|
70
|
+
// `Reventless.Projection.DcbSource.Make` helper is also available but requires
|
|
71
|
+
// binding the inline definition to a name first (ReScript abstract-type
|
|
72
|
+
// limitation), and the hand-rolled form is shorter for most cases.
|
|
73
|
+
// ─────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
module ProductCatalogDcbSource = {
|
|
76
|
+
module Id = Reventless.Id.String
|
|
77
|
+
let name = "ProductCatalogDcbEventLog"
|
|
78
|
+
|
|
79
|
+
@schema
|
|
80
|
+
type event = ProductAdded({productId: string, name: string})
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ─────────────────────────────────────────────────────────────
|
|
84
|
+
// ReadModel spec
|
|
85
|
+
// ─────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
module ProductsReadModelSpec = {
|
|
88
|
+
module Id = Reventless.Id.String
|
|
89
|
+
let name = "TestProductsReadModel"
|
|
90
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
91
|
+
|
|
92
|
+
@schema
|
|
93
|
+
type state = {productId: string, name: string}
|
|
94
|
+
|
|
95
|
+
let config = ReadModel.config()
|
|
96
|
+
let subIdConfig = None
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
module ProductsFromDcb = Mapping.Make(
|
|
100
|
+
ProductCatalogDcbSource,
|
|
101
|
+
ProductsReadModelSpec,
|
|
102
|
+
{
|
|
103
|
+
let project = (msg: Message.event'<string, ProductCatalogDcbSource.event>) =>
|
|
104
|
+
switch msg.event {
|
|
105
|
+
| ProductAdded({productId, name}) =>
|
|
106
|
+
Set(productId, ({productId, name}: ProductsReadModelSpec.state))
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
module ProductsMappings: Mappings with module Target := ProductsReadModelSpec = {
|
|
112
|
+
module Mappings = Mappings.Make(ProductsReadModelSpec)
|
|
113
|
+
module type Mapping = Mappings.Mapping
|
|
114
|
+
let moduleUrl: string = %raw(`import.meta.url`)
|
|
115
|
+
let mappings: array<module(Mapping)> = [module(ProductsFromDcb)]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─────────────────────────────────────────────────────────────
|
|
119
|
+
// Bus + Pulumi mock setup
|
|
120
|
+
// ─────────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
module Bus = LocalBus.Make()
|
|
123
|
+
let _ = TestRunner.setup()
|
|
124
|
+
|
|
125
|
+
// ─────────────────────────────────────────────────────────────
|
|
126
|
+
// Build DcbEventLog (~name="ProductCatalog") and StateChangeSlice
|
|
127
|
+
// ─────────────────────────────────────────────────────────────
|
|
128
|
+
|
|
129
|
+
module DcbLogMaker = DcbEventLog_Builder.Make(Bus)
|
|
130
|
+
let dcbEventLog = DcbLogMaker.make(
|
|
131
|
+
~name="ProductCatalog",
|
|
132
|
+
~partitionTag=Reventless.DcbTag.Simple({key: "productId"}),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
module AddProductMaker = StateChangeSlice_Builder.Make(AddProductSpec, AddProductBehavior)
|
|
136
|
+
|
|
137
|
+
let publishJsons: ReventlessInfra.CommandTopic.publishJsons = async cmdJsons => {
|
|
138
|
+
let _ = await cmdJsons
|
|
139
|
+
->Array.map(async cmdJson => {
|
|
140
|
+
let typeName = switch cmdJson.commandJson {
|
|
141
|
+
| JSON.Object(dict) =>
|
|
142
|
+
dict
|
|
143
|
+
->Dict.get("TAG")
|
|
144
|
+
->Option.flatMap(j =>
|
|
145
|
+
switch j {
|
|
146
|
+
| JSON.String(s) => Some(s)
|
|
147
|
+
| _ => None
|
|
148
|
+
}
|
|
149
|
+
)
|
|
150
|
+
->Option.getOr("")
|
|
151
|
+
| _ => ""
|
|
152
|
+
}
|
|
153
|
+
let fullBody = JSON.Encode.object(
|
|
154
|
+
Dict.fromArray([
|
|
155
|
+
("id", JSON.Encode.string(cmdJson.id)),
|
|
156
|
+
("meta", cmdJson.meta->S.reverseConvertToJsonOrThrow(Reventless.Message.metaSchema)),
|
|
157
|
+
("command", cmdJson.commandJson),
|
|
158
|
+
]),
|
|
159
|
+
)
|
|
160
|
+
let handlers = ReventlessCore.CommandTopic.getHandlers(typeName)
|
|
161
|
+
let _ = await handlers
|
|
162
|
+
->Array.map(async entry => {
|
|
163
|
+
let item: ReventlessInfra.CommandTopic.topicItem<JSON.t> = {
|
|
164
|
+
reference: cmdJson.id,
|
|
165
|
+
command: fullBody,
|
|
166
|
+
}
|
|
167
|
+
let _ = await entry.handler(Stream.fromIterable([item]))->Effect.runPromise
|
|
168
|
+
})
|
|
169
|
+
->Promise.all
|
|
170
|
+
})
|
|
171
|
+
->Promise.all
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let publishJsonsOutput = publishJsons->Pulumi.Output.make
|
|
175
|
+
let _addProductSlice = AddProductMaker.make(~dcbEventLog, ~publishJsons=publishJsonsOutput)
|
|
176
|
+
|
|
177
|
+
// ─────────────────────────────────────────────────────────────
|
|
178
|
+
// Build allEventTopics and the ReadModel.
|
|
179
|
+
// Mirrors how Plugin_Builder merges the DCB EventTopic into allEventTopics
|
|
180
|
+
// under the key `<name> ++ "DcbEventLog"`.
|
|
181
|
+
// ─────────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
let dcbTopicOutputs: ReventlessInfra.EventTopic.outputs = (dcbEventLog->ReventlessInfra.Component.outputs).eventTopic
|
|
184
|
+
|
|
185
|
+
let allEventTopics: ReventlessInfra.EventTopic.allOutputs = Dict.fromArray([
|
|
186
|
+
("ProductCatalogDcbEventLog", dcbTopicOutputs),
|
|
187
|
+
])
|
|
188
|
+
|
|
189
|
+
module ReadModelMaker = ReadModel_Builder.Make(Bus)
|
|
190
|
+
module ProductsReadModel = ReadModelMaker.Make(ProductsReadModelSpec, ProductsMappings)
|
|
191
|
+
let rm = ProductsReadModel.make(~api=(), ~apiRole=(), ~allEventTopics)
|
|
192
|
+
|
|
193
|
+
// ─────────────────────────────────────────────────────────────
|
|
194
|
+
// Test helpers
|
|
195
|
+
// ─────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
let dispatch = async (commandJson, id) =>
|
|
198
|
+
await publishJsons([{Reventless.Message.id, meta: testMeta, commandJson}])
|
|
199
|
+
|
|
200
|
+
let addProductCmd = (productId, name) =>
|
|
201
|
+
AddProductSpec.AddProduct({productId, name})
|
|
202
|
+
->S.reverseConvertToJsonOrThrow(AddProductSpec.commandSchema)
|
|
203
|
+
|
|
204
|
+
let loadState = async productId => {
|
|
205
|
+
switch Bus.getQueryDb("TestProductsReadModel") {
|
|
206
|
+
| None => []
|
|
207
|
+
| Some(ops) =>
|
|
208
|
+
let states =
|
|
209
|
+
await ops.loadStream(productId)
|
|
210
|
+
->Stream.runCollect
|
|
211
|
+
->Effect.catchAll(_ => Effect.succeed([]))
|
|
212
|
+
->Effect.runPromise
|
|
213
|
+
states->Array.map(json => json->S.parseJsonOrThrow(ProductsReadModelSpec.stateSchema))
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as S from "sury/src/S.res.mjs";
|
|
4
|
+
import * as Stream from "@reventlessdev/rescript-effect/src/Stream.res.mjs";
|
|
5
|
+
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Stream$1 from "effect/Stream";
|
|
9
|
+
import * as Pulumi from "@pulumi/pulumi";
|
|
10
|
+
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
11
|
+
import * as Message$Reventless from "@reventlessdev/reventless-spec/src/types/Message.res.mjs";
|
|
12
|
+
import * as ReadModel$Reventless from "@reventlessdev/reventless-spec/src/components/ReadModel.res.mjs";
|
|
13
|
+
import * as Projection$Reventless from "@reventlessdev/reventless-spec/src/types/Projection.res.mjs";
|
|
14
|
+
import * as LocalBus$ReventlessLocal from "../../../src/adapter/LocalBus.res.mjs";
|
|
15
|
+
import * as Component$ReventlessInfra from "@reventlessdev/reventless-infra/src/components/Component.res.mjs";
|
|
16
|
+
import * as TestRunner$ReventlessLocal from "../../../src/test/TestRunner.res.mjs";
|
|
17
|
+
import * as CommandTopic$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic.res.mjs";
|
|
18
|
+
import * as TestFixtures$ReventlessLocal from "../../TestFixtures.res.mjs";
|
|
19
|
+
import * as ReadModel_Builder$ReventlessLocal from "../../../src/components/ReadModel_Builder.res.mjs";
|
|
20
|
+
import * as DcbEventLog_Builder$ReventlessLocal from "../../../src/components/DcbEventLog_Builder.res.mjs";
|
|
21
|
+
import * as StateChangeSlice_Builder$ReventlessLocal from "../../../src/components/StateChangeSlice_Builder.res.mjs";
|
|
22
|
+
|
|
23
|
+
let name = "AddProduct";
|
|
24
|
+
|
|
25
|
+
let moduleUrl = import.meta.url;
|
|
26
|
+
|
|
27
|
+
let eventSchema = S.schema(s => ({
|
|
28
|
+
TAG: "ProductAdded",
|
|
29
|
+
productId: s.m(DcbTag$Reventless.string),
|
|
30
|
+
name: s.m(S.string)
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
let consumedEventSchema = S.literal("ProductAdded");
|
|
34
|
+
|
|
35
|
+
let commandSchema = S.schema(s => ({
|
|
36
|
+
TAG: "AddProduct",
|
|
37
|
+
productId: s.m(DcbTag$Reventless.string),
|
|
38
|
+
name: s.m(S.string)
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
let errorSchema = S.literal("ProductAlreadyExists");
|
|
42
|
+
|
|
43
|
+
function commandAuthorization(param) {
|
|
44
|
+
return "AllowAuthenticated";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let AddProductSpec = {
|
|
48
|
+
name: name,
|
|
49
|
+
Id: undefined,
|
|
50
|
+
moduleUrl: moduleUrl,
|
|
51
|
+
eventSchema: eventSchema,
|
|
52
|
+
consumedEventSchema: consumedEventSchema,
|
|
53
|
+
errorSchema: errorSchema,
|
|
54
|
+
commandSchema: commandSchema,
|
|
55
|
+
commandAuthorization: commandAuthorization,
|
|
56
|
+
readConsistency: "EscalateOnRetry"
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
let moduleUrl$1 = import.meta.url;
|
|
60
|
+
|
|
61
|
+
function evolve(_state, _event) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function decide(state, command) {
|
|
66
|
+
if (state) {
|
|
67
|
+
return {
|
|
68
|
+
TAG: "Error",
|
|
69
|
+
_0: "ProductAlreadyExists"
|
|
70
|
+
};
|
|
71
|
+
} else {
|
|
72
|
+
return {
|
|
73
|
+
TAG: "Ok",
|
|
74
|
+
_0: [{
|
|
75
|
+
TAG: "ProductAdded",
|
|
76
|
+
productId: command.productId,
|
|
77
|
+
name: command.name
|
|
78
|
+
}]
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
let AddProductBehavior = {
|
|
84
|
+
Spec: undefined,
|
|
85
|
+
moduleUrl: moduleUrl$1,
|
|
86
|
+
initialState: false,
|
|
87
|
+
evolve: evolve,
|
|
88
|
+
decide: decide
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let name$1 = "ProductCatalogDcbEventLog";
|
|
92
|
+
|
|
93
|
+
let eventSchema$1 = S.schema(s => ({
|
|
94
|
+
TAG: "ProductAdded",
|
|
95
|
+
productId: s.m(S.string),
|
|
96
|
+
name: s.m(S.string)
|
|
97
|
+
}));
|
|
98
|
+
|
|
99
|
+
let ProductCatalogDcbSource = {
|
|
100
|
+
Id: undefined,
|
|
101
|
+
name: name$1,
|
|
102
|
+
eventSchema: eventSchema$1
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
let name$2 = "TestProductsReadModel";
|
|
106
|
+
|
|
107
|
+
let moduleUrl$2 = import.meta.url;
|
|
108
|
+
|
|
109
|
+
let stateSchema = S.schema(s => ({
|
|
110
|
+
productId: s.m(S.string),
|
|
111
|
+
name: s.m(S.string)
|
|
112
|
+
}));
|
|
113
|
+
|
|
114
|
+
let config = ReadModel$Reventless.config(undefined, undefined, undefined);
|
|
115
|
+
|
|
116
|
+
let ProductsReadModelSpec = {
|
|
117
|
+
Id: undefined,
|
|
118
|
+
name: name$2,
|
|
119
|
+
moduleUrl: moduleUrl$2,
|
|
120
|
+
stateSchema: stateSchema,
|
|
121
|
+
config: config,
|
|
122
|
+
subIdConfig: undefined,
|
|
123
|
+
authorization: "AllowAuthenticated",
|
|
124
|
+
visibility: "Public"
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
function project(msg) {
|
|
128
|
+
let match = msg.event;
|
|
129
|
+
let productId = match.productId;
|
|
130
|
+
return {
|
|
131
|
+
TAG: "Set",
|
|
132
|
+
_0: productId,
|
|
133
|
+
_1: {
|
|
134
|
+
productId: productId,
|
|
135
|
+
name: match.name
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let ProductsFromDcb = Projection$Reventless.Mapping.Make({
|
|
141
|
+
Id: Id$Reventless.$$String,
|
|
142
|
+
name: name$1,
|
|
143
|
+
eventSchema: eventSchema$1
|
|
144
|
+
})({
|
|
145
|
+
Id: Id$Reventless.$$String,
|
|
146
|
+
name: name$2,
|
|
147
|
+
stateSchema: stateSchema,
|
|
148
|
+
subIdConfig: undefined
|
|
149
|
+
})({
|
|
150
|
+
project: project
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
Projection$Reventless.Mappings.Make({
|
|
154
|
+
Id: Id$Reventless.$$String,
|
|
155
|
+
name: name$2,
|
|
156
|
+
stateSchema: stateSchema,
|
|
157
|
+
subIdConfig: undefined
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let moduleUrl$3 = import.meta.url;
|
|
161
|
+
|
|
162
|
+
let mappings = [ProductsFromDcb];
|
|
163
|
+
|
|
164
|
+
let ProductsMappings = {
|
|
165
|
+
moduleUrl: moduleUrl$3,
|
|
166
|
+
mappings: mappings
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
let Bus = LocalBus$ReventlessLocal.Make({});
|
|
170
|
+
|
|
171
|
+
TestRunner$ReventlessLocal.setup();
|
|
172
|
+
|
|
173
|
+
let DcbLogMaker = DcbEventLog_Builder$ReventlessLocal.Make(Bus);
|
|
174
|
+
|
|
175
|
+
let dcbEventLog = DcbLogMaker.make("ProductCatalog", undefined, {
|
|
176
|
+
TAG: "Simple",
|
|
177
|
+
_0: {
|
|
178
|
+
key: "productId"
|
|
179
|
+
}
|
|
180
|
+
}, undefined);
|
|
181
|
+
|
|
182
|
+
let AddProductMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
|
|
183
|
+
name: name,
|
|
184
|
+
moduleUrl: moduleUrl,
|
|
185
|
+
Id: Id$Reventless.$$String,
|
|
186
|
+
consumedEventSchema: consumedEventSchema,
|
|
187
|
+
errorSchema: errorSchema,
|
|
188
|
+
eventSchema: eventSchema,
|
|
189
|
+
commandSchema: commandSchema,
|
|
190
|
+
commandAuthorization: commandAuthorization,
|
|
191
|
+
readConsistency: "EscalateOnRetry"
|
|
192
|
+
})({
|
|
193
|
+
initialState: false,
|
|
194
|
+
evolve: evolve,
|
|
195
|
+
decide: decide,
|
|
196
|
+
moduleUrl: moduleUrl$1
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
async function publishJsons(cmdJsons) {
|
|
200
|
+
await Promise.all(cmdJsons.map(async cmdJson => {
|
|
201
|
+
let dict = cmdJson.commandJson;
|
|
202
|
+
let typeName;
|
|
203
|
+
typeName = typeof dict === "object" && dict !== null && !Array.isArray(dict) ? Stdlib_Option.getOr(Stdlib_Option.flatMap(dict["TAG"], j => {
|
|
204
|
+
if (typeof j === "string") {
|
|
205
|
+
return j;
|
|
206
|
+
}
|
|
207
|
+
}), "") : "";
|
|
208
|
+
let fullBody = Object.fromEntries([
|
|
209
|
+
[
|
|
210
|
+
"id",
|
|
211
|
+
cmdJson.id
|
|
212
|
+
],
|
|
213
|
+
[
|
|
214
|
+
"meta",
|
|
215
|
+
S.reverseConvertToJsonOrThrow(cmdJson.meta, Message$Reventless.metaSchema)
|
|
216
|
+
],
|
|
217
|
+
[
|
|
218
|
+
"command",
|
|
219
|
+
cmdJson.commandJson
|
|
220
|
+
]
|
|
221
|
+
]);
|
|
222
|
+
let handlers = CommandTopic$ReventlessCore.getHandlers(typeName);
|
|
223
|
+
await Promise.all(handlers.map(async entry => {
|
|
224
|
+
let item_reference = cmdJson.id;
|
|
225
|
+
let item = {
|
|
226
|
+
command: fullBody,
|
|
227
|
+
reference: item_reference
|
|
228
|
+
};
|
|
229
|
+
await Effect.runPromise(entry.handler(Stream$1.fromIterable([item])));
|
|
230
|
+
}));
|
|
231
|
+
}));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
let publishJsonsOutput = Pulumi.output(publishJsons);
|
|
235
|
+
|
|
236
|
+
let _addProductSlice = AddProductMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined);
|
|
237
|
+
|
|
238
|
+
let dcbTopicOutputs = Component$ReventlessInfra.outputs(dcbEventLog).eventTopic;
|
|
239
|
+
|
|
240
|
+
let allEventTopics = Object.fromEntries([[
|
|
241
|
+
"ProductCatalogDcbEventLog",
|
|
242
|
+
dcbTopicOutputs
|
|
243
|
+
]]);
|
|
244
|
+
|
|
245
|
+
let ReadModelMaker = ReadModel_Builder$ReventlessLocal.Make(Bus);
|
|
246
|
+
|
|
247
|
+
let ProductsReadModel = ReadModelMaker.Make({
|
|
248
|
+
Id: Id$Reventless.$$String,
|
|
249
|
+
name: name$2,
|
|
250
|
+
moduleUrl: moduleUrl$2,
|
|
251
|
+
stateSchema: stateSchema,
|
|
252
|
+
config: config,
|
|
253
|
+
subIdConfig: undefined,
|
|
254
|
+
authorization: "AllowAuthenticated",
|
|
255
|
+
visibility: "Public"
|
|
256
|
+
})(ProductsMappings);
|
|
257
|
+
|
|
258
|
+
let rm = ProductsReadModel.make(undefined, undefined, allEventTopics, undefined);
|
|
259
|
+
|
|
260
|
+
async function dispatch(commandJson, id) {
|
|
261
|
+
return await publishJsons([{
|
|
262
|
+
id: id,
|
|
263
|
+
meta: TestFixtures$ReventlessLocal.testMeta,
|
|
264
|
+
commandJson: commandJson
|
|
265
|
+
}]);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function addProductCmd(productId, name) {
|
|
269
|
+
return S.reverseConvertToJsonOrThrow({
|
|
270
|
+
TAG: "AddProduct",
|
|
271
|
+
productId: productId,
|
|
272
|
+
name: name
|
|
273
|
+
}, commandSchema);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function loadState(productId) {
|
|
277
|
+
let ops = Bus.getQueryDb("TestProductsReadModel");
|
|
278
|
+
if (ops === undefined) {
|
|
279
|
+
return [];
|
|
280
|
+
}
|
|
281
|
+
let states = await Effect.runPromise(Effect.catchAll(Stream.runCollect(ops.loadStream(productId)), param => Effect.succeed([])));
|
|
282
|
+
return states.map(json => S.parseJsonOrThrow(json, stateSchema));
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export {
|
|
286
|
+
AddProductSpec,
|
|
287
|
+
AddProductBehavior,
|
|
288
|
+
ProductCatalogDcbSource,
|
|
289
|
+
ProductsReadModelSpec,
|
|
290
|
+
ProductsFromDcb,
|
|
291
|
+
ProductsMappings,
|
|
292
|
+
Bus,
|
|
293
|
+
DcbLogMaker,
|
|
294
|
+
dcbEventLog,
|
|
295
|
+
AddProductMaker,
|
|
296
|
+
publishJsons,
|
|
297
|
+
publishJsonsOutput,
|
|
298
|
+
_addProductSlice,
|
|
299
|
+
dcbTopicOutputs,
|
|
300
|
+
allEventTopics,
|
|
301
|
+
ReadModelMaker,
|
|
302
|
+
ProductsReadModel,
|
|
303
|
+
rm,
|
|
304
|
+
dispatch,
|
|
305
|
+
addProductCmd,
|
|
306
|
+
loadState,
|
|
307
|
+
}
|
|
308
|
+
/* moduleUrl Not a pure module */
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// E2E test: a DCB StateChangeSlice's events flow through the DcbEventLog's
|
|
2
|
+
// EventTopic and land in a ReadModel projection — the very pattern Plan 03
|
|
3
|
+
// is meant to enable.
|
|
4
|
+
//
|
|
5
|
+
// This test will silently pass-but-project-nothing if the Phase 1.5 fix
|
|
6
|
+
// (meta.service = `<name>DcbEventLog`) ever regresses, so it doubles as a
|
|
7
|
+
// regression vehicle for that fix.
|
|
8
|
+
|
|
9
|
+
open JestGlobals
|
|
10
|
+
open DcbReadModelE2EFixtures
|
|
11
|
+
|
|
12
|
+
describe("DCB → ReadModel E2E:", () => {
|
|
13
|
+
let _ = beforeAllAsync(async () => {
|
|
14
|
+
let _ = await dcbEventLog->DcbLogMaker.operations->TestRunner.resolve
|
|
15
|
+
let _ = await rm->ProductsReadModel.operations->TestRunner.resolve
|
|
16
|
+
// Wait for the ReadModel's EventCollector to register its bus subscription.
|
|
17
|
+
let resource = dcbTopicOutputs.resources->Array.getUnsafe(0)
|
|
18
|
+
let _ = await resource.name->TestRunner.resolve
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
testPromise("AddProduct command updates the ReadModel state", async () => {
|
|
22
|
+
await dispatch(addProductCmd("prod-1", "Laptop"), "prod-1")
|
|
23
|
+
let states = await loadState("prod-1")
|
|
24
|
+
expect(states->Array.length)->toBe(1)
|
|
25
|
+
let state = states->Array.getUnsafe(0)
|
|
26
|
+
expect(state.productId)->toBe("prod-1")
|
|
27
|
+
expect(state.name)->toBe("Laptop")
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
testPromise("second AddProduct for the same id is a no-op (decide returns Error)", async () => {
|
|
31
|
+
await dispatch(addProductCmd("prod-1", "Different name"), "prod-1")
|
|
32
|
+
let states = await loadState("prod-1")
|
|
33
|
+
expect(states->Array.length)->toBe(1)
|
|
34
|
+
let state = states->Array.getUnsafe(0)
|
|
35
|
+
expect(state.name)->toBe("Laptop")
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
testPromise("a different product id gets its own entry", async () => {
|
|
39
|
+
await dispatch(addProductCmd("prod-2", "Headphones"), "prod-2")
|
|
40
|
+
let states = await loadState("prod-2")
|
|
41
|
+
expect(states->Array.length)->toBe(1)
|
|
42
|
+
let state = states->Array.getUnsafe(0)
|
|
43
|
+
expect(state.name)->toBe("Headphones")
|
|
44
|
+
})
|
|
45
|
+
})
|