@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,208 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Nodefs from "node:fs";
|
|
4
|
+
import * as Nodeos from "node:os";
|
|
5
|
+
import * as Nodepath from "node:path";
|
|
6
|
+
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
7
|
+
import * as LocalAuth$ReventlessLocal from "../../src/adapter/Auth/LocalAuth.res.mjs";
|
|
8
|
+
import * as UserStore$ReventlessLocal from "../../src/adapter/Auth/UserStore.res.mjs";
|
|
9
|
+
|
|
10
|
+
function resetAll() {
|
|
11
|
+
LocalAuth$ReventlessLocal.resetUsers();
|
|
12
|
+
LocalAuth$ReventlessLocal.Login.resetStore();
|
|
13
|
+
UserStore$ReventlessLocal.resetResolution();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function _writeYamlFile(contents) {
|
|
17
|
+
let dir = Nodefs.mkdtempSync(Nodepath.join(Nodeos.tmpdir(), "reventless-userstore-"));
|
|
18
|
+
let path = Nodepath.join(dir, "users.yaml");
|
|
19
|
+
Nodefs.writeFileSync(path, contents, "utf8");
|
|
20
|
+
return path;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
globalThis.test("parseString decodes a well-formed YAML document", async () => {
|
|
24
|
+
let entries = UserStore$ReventlessLocal.parseString(`
|
|
25
|
+
- username: alice
|
|
26
|
+
password: alice-pw
|
|
27
|
+
groups: [Editor]
|
|
28
|
+
- username: bob
|
|
29
|
+
password: bob-pw
|
|
30
|
+
groups: []
|
|
31
|
+
`);
|
|
32
|
+
if (entries.TAG !== "Ok") {
|
|
33
|
+
return Stdlib_JsError.throwWithMessage("parseString failed: " + entries._0);
|
|
34
|
+
}
|
|
35
|
+
let entries$1 = entries._0;
|
|
36
|
+
globalThis.expect(entries$1.length).toEqual(2);
|
|
37
|
+
let first = entries$1[0];
|
|
38
|
+
globalThis.expect(first.username).toEqual("alice");
|
|
39
|
+
globalThis.expect(first.password).toEqual("alice-pw");
|
|
40
|
+
globalThis.expect(first.groups).toEqual(["Editor"]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
globalThis.test("parseString rejects malformed YAML", async () => {
|
|
44
|
+
let match = UserStore$ReventlessLocal.parseString("not: valid: yaml: at all: ::");
|
|
45
|
+
if (match.TAG === "Ok") {
|
|
46
|
+
return Stdlib_JsError.throwWithMessage("expected Error for malformed YAML");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
globalThis.test("parseString rejects entries missing required fields", async () => {
|
|
51
|
+
let match = UserStore$ReventlessLocal.parseString(`
|
|
52
|
+
- username: alice
|
|
53
|
+
groups: [Editor]
|
|
54
|
+
`);
|
|
55
|
+
if (match.TAG === "Ok") {
|
|
56
|
+
return Stdlib_JsError.throwWithMessage("expected Error for missing password");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
globalThis.test("load(~users) registers credentials and returns InlineUsers", async () => {
|
|
61
|
+
resetAll();
|
|
62
|
+
let entries = [
|
|
63
|
+
{
|
|
64
|
+
username: "alice",
|
|
65
|
+
password: "alice-pw",
|
|
66
|
+
groups: ["Editor"]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
username: "bob",
|
|
70
|
+
password: "bob-pw",
|
|
71
|
+
groups: [],
|
|
72
|
+
userId: "u-bob"
|
|
73
|
+
}
|
|
74
|
+
];
|
|
75
|
+
let msg = UserStore$ReventlessLocal.load(entries, undefined, undefined);
|
|
76
|
+
if (msg.TAG === "Ok") {
|
|
77
|
+
let tmp = msg._0;
|
|
78
|
+
if (typeof tmp === "object" || tmp !== "InlineUsers") {
|
|
79
|
+
Stdlib_JsError.throwWithMessage("expected InlineUsers");
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
Stdlib_JsError.throwWithMessage("load failed: " + msg._0);
|
|
83
|
+
}
|
|
84
|
+
let result = await LocalAuth$ReventlessLocal.Login.issue("alice", "alice-pw");
|
|
85
|
+
if (result.TAG !== "Ok") {
|
|
86
|
+
Stdlib_JsError.throwWithMessage("issue rejected loaded credentials: " + result._0);
|
|
87
|
+
}
|
|
88
|
+
let identity = LocalAuth$ReventlessLocal.lookupUser("bob");
|
|
89
|
+
if (identity !== undefined) {
|
|
90
|
+
globalThis.expect(identity.userId).toEqual("u-bob");
|
|
91
|
+
return;
|
|
92
|
+
} else {
|
|
93
|
+
return Stdlib_JsError.throwWithMessage("bob not in user registry");
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
globalThis.test("load(~usersFile) reads a YAML file from disk", async () => {
|
|
98
|
+
resetAll();
|
|
99
|
+
let path = _writeYamlFile(`
|
|
100
|
+
- username: carol
|
|
101
|
+
password: carol-pw
|
|
102
|
+
groups: [Admin]
|
|
103
|
+
`);
|
|
104
|
+
let msg = UserStore$ReventlessLocal.load(undefined, path, undefined);
|
|
105
|
+
if (msg.TAG === "Ok") {
|
|
106
|
+
let p = msg._0;
|
|
107
|
+
if (typeof p !== "object" || p.TAG !== "UsersFile") {
|
|
108
|
+
Stdlib_JsError.throwWithMessage("expected UsersFile resolution");
|
|
109
|
+
} else {
|
|
110
|
+
globalThis.expect(p._0).toEqual(path);
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
Stdlib_JsError.throwWithMessage("load failed: " + msg._0);
|
|
114
|
+
}
|
|
115
|
+
let result = await LocalAuth$ReventlessLocal.Login.issue("carol", "carol-pw");
|
|
116
|
+
if (result.TAG !== "Ok") {
|
|
117
|
+
Stdlib_JsError.throwWithMessage("issue rejected file-loaded credentials: " + result._0);
|
|
118
|
+
}
|
|
119
|
+
Nodefs.rmSync(path, {
|
|
120
|
+
force: true
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
globalThis.test("load(~usersFile) returns Error when the file is missing", async () => {
|
|
125
|
+
resetAll();
|
|
126
|
+
let match = UserStore$ReventlessLocal.load(undefined, "/tmp/does-not-exist-reventless.yaml", undefined);
|
|
127
|
+
if (match.TAG === "Ok") {
|
|
128
|
+
return Stdlib_JsError.throwWithMessage("expected Error for missing file");
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
globalThis.test("load() with no args + no default file returns Empty", async () => {
|
|
133
|
+
resetAll();
|
|
134
|
+
let msg = UserStore$ReventlessLocal.load(undefined, undefined, undefined);
|
|
135
|
+
if (msg.TAG !== "Ok") {
|
|
136
|
+
return Stdlib_JsError.throwWithMessage("load failed: " + msg._0);
|
|
137
|
+
}
|
|
138
|
+
let tmp = msg._0;
|
|
139
|
+
if (typeof tmp !== "object") {
|
|
140
|
+
if (tmp === "InlineUsers") {
|
|
141
|
+
return Stdlib_JsError.throwWithMessage("expected Empty or DefaultFile");
|
|
142
|
+
} else {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
} else if (tmp.TAG === "UsersFile") {
|
|
146
|
+
return Stdlib_JsError.throwWithMessage("expected Empty or DefaultFile");
|
|
147
|
+
} else {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
globalThis.test("inline users win over a file path", async () => {
|
|
153
|
+
resetAll();
|
|
154
|
+
let path = _writeYamlFile(`
|
|
155
|
+
- username: file-user
|
|
156
|
+
password: file-pw
|
|
157
|
+
groups: []
|
|
158
|
+
`);
|
|
159
|
+
let inline = [{
|
|
160
|
+
username: "inline-user",
|
|
161
|
+
password: "inline-pw",
|
|
162
|
+
groups: []
|
|
163
|
+
}];
|
|
164
|
+
let msg = UserStore$ReventlessLocal.load(inline, path, undefined);
|
|
165
|
+
if (msg.TAG === "Ok") {
|
|
166
|
+
let tmp = msg._0;
|
|
167
|
+
if (typeof tmp === "object" || tmp !== "InlineUsers") {
|
|
168
|
+
Stdlib_JsError.throwWithMessage("expected InlineUsers (inline should win)");
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
Stdlib_JsError.throwWithMessage("load failed: " + msg._0);
|
|
172
|
+
}
|
|
173
|
+
let match = LocalAuth$ReventlessLocal.lookupUser("file-user");
|
|
174
|
+
if (match !== undefined) {
|
|
175
|
+
Stdlib_JsError.throwWithMessage("file-user leaked into registry despite inline override");
|
|
176
|
+
}
|
|
177
|
+
let match$1 = LocalAuth$ReventlessLocal.lookupUser("inline-user");
|
|
178
|
+
if (match$1 !== undefined) {
|
|
179
|
+
|
|
180
|
+
} else {
|
|
181
|
+
Stdlib_JsError.throwWithMessage("inline-user missing from registry");
|
|
182
|
+
}
|
|
183
|
+
Nodefs.rmSync(path, {
|
|
184
|
+
force: true
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
globalThis.test("autoLoadOnce is a no-op after an explicit load", async () => {
|
|
189
|
+
resetAll();
|
|
190
|
+
UserStore$ReventlessLocal.load([{
|
|
191
|
+
username: "x",
|
|
192
|
+
password: "x",
|
|
193
|
+
groups: []
|
|
194
|
+
}], undefined, undefined);
|
|
195
|
+
UserStore$ReventlessLocal.autoLoadOnce();
|
|
196
|
+
let match = LocalAuth$ReventlessLocal.lookupUser("x");
|
|
197
|
+
if (match !== undefined) {
|
|
198
|
+
return;
|
|
199
|
+
} else {
|
|
200
|
+
return Stdlib_JsError.throwWithMessage("autoLoadOnce wiped explicit registration");
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export {
|
|
205
|
+
resetAll,
|
|
206
|
+
_writeYamlFile,
|
|
207
|
+
}
|
|
208
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Tests for the bounded PubSub mode of LocalBus (Phase G).
|
|
2
|
+
// Verifies that MakeBounded({let capacity = n}) delivers messages correctly, fans out to all
|
|
3
|
+
// subscribers, and exerts backpressure when subscriber queues are full.
|
|
4
|
+
//
|
|
5
|
+
// Timing note: bounded mode resolves publishEvent in ~3 microtask ticks
|
|
6
|
+
// (1 extra vs unbounded) because PubSub.publish runs inside Effect.runPromise
|
|
7
|
+
// rather than Effect.runSync.
|
|
8
|
+
|
|
9
|
+
open JestGlobals
|
|
10
|
+
|
|
11
|
+
let _ = TestRunner.setup()
|
|
12
|
+
|
|
13
|
+
let defaultMeta: Reventless.Message.meta = {
|
|
14
|
+
service: "test",
|
|
15
|
+
time: "",
|
|
16
|
+
ip: "",
|
|
17
|
+
user: "test",
|
|
18
|
+
msgId: "",
|
|
19
|
+
correlationId: "",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("LocalBus bounded PubSub (Phase G)", () => {
|
|
23
|
+
describe("basic delivery", () => {
|
|
24
|
+
testPromise("delivers a message to a subscriber in bounded mode", async () => {
|
|
25
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 10})
|
|
26
|
+
let delivered = ref(false)
|
|
27
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
28
|
+
delivered := true
|
|
29
|
+
})
|
|
30
|
+
let _ = await TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
31
|
+
expect(delivered.contents)->toBe(true)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
testPromise("bounded mode with no subscribers returns immediately", async () => {
|
|
35
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 5})
|
|
36
|
+
// No subscribers — should not hang
|
|
37
|
+
await TestBus.publishEvent("empty-topic", "svc", defaultMeta, JSON.Null)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
testPromise("passes service, meta, and json to the subscriber handler", async () => {
|
|
41
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 10})
|
|
42
|
+
let capturedService = ref("")
|
|
43
|
+
let capturedJson = ref(JSON.Null)
|
|
44
|
+
TestBus.subscribeToEvents("T", async (svc, _meta, json) => {
|
|
45
|
+
capturedService := svc
|
|
46
|
+
capturedJson := json
|
|
47
|
+
})
|
|
48
|
+
let payload = JSON.parseOrThrow("{\"x\":42}")
|
|
49
|
+
let _ = await TestBus.publishEvent("T", "my-service", defaultMeta, payload)
|
|
50
|
+
expect(capturedService.contents)->toBe("my-service")
|
|
51
|
+
expect(capturedJson.contents)->toEqual(payload)
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe("fan-out", () => {
|
|
56
|
+
testPromise("delivers to all subscribers on the same topic", async () => {
|
|
57
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 10})
|
|
58
|
+
let count = ref(0)
|
|
59
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
60
|
+
count := count.contents + 1
|
|
61
|
+
})
|
|
62
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
63
|
+
count := count.contents + 1
|
|
64
|
+
})
|
|
65
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
66
|
+
count := count.contents + 1
|
|
67
|
+
})
|
|
68
|
+
let _ = await TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
69
|
+
expect(count.contents)->toBe(3)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
testPromise("topic isolation preserved in bounded mode", async () => {
|
|
73
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 10})
|
|
74
|
+
let countA = ref(0)
|
|
75
|
+
let countB = ref(0)
|
|
76
|
+
TestBus.subscribeToEvents("topic-a", async (_, _, _) => {
|
|
77
|
+
countA := countA.contents + 1
|
|
78
|
+
})
|
|
79
|
+
TestBus.subscribeToEvents("topic-b", async (_, _, _) => {
|
|
80
|
+
countB := countB.contents + 1
|
|
81
|
+
})
|
|
82
|
+
let _ = await TestBus.publishEvent("topic-a", "svc", defaultMeta, JSON.Null)
|
|
83
|
+
expect(countA.contents)->toBe(1)
|
|
84
|
+
expect(countB.contents)->toBe(0)
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe("backpressure", () => {
|
|
89
|
+
testPromise("publisher suspends when subscriber queue is full (capacity=1)", async () => {
|
|
90
|
+
// capacity=1: subscriber queue can buffer 1 message while the subscriber is processing.
|
|
91
|
+
// Timeline:
|
|
92
|
+
// p1 publish: drain fiber D has a pending Queue.take → msg1 goes to D directly
|
|
93
|
+
// p2 publish: no pending take (D is processing msg1, blocked on gate) → msg2 buffered
|
|
94
|
+
// p3 publish: queue is full (msg2 there, capacity=1) → F3 SUSPENDS
|
|
95
|
+
// After opening gate: msg1 processed → done_1 → D takes msg2 → F3 wakes, delivers msg3
|
|
96
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 1})
|
|
97
|
+
let processedCount = ref(0)
|
|
98
|
+
// Gate Deferred: subscriber blocks until explicitly opened
|
|
99
|
+
let gate: Deferred.t<unit, unit> = Deferred.make()->Effect.runSync
|
|
100
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
101
|
+
let _ = await Deferred.await_(gate)->Effect.runPromise
|
|
102
|
+
processedCount := processedCount.contents + 1
|
|
103
|
+
})
|
|
104
|
+
// Start 3 publishes without awaiting.
|
|
105
|
+
// p1: D's pending Queue.take resolves with msg1; D starts processing (blocks on gate)
|
|
106
|
+
// p2: msg2 buffered in queue (capacity=1, fits since D already took msg1)
|
|
107
|
+
// p3: queue full → F3 fiber suspends inside PubSub.publish
|
|
108
|
+
let p1 = TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
109
|
+
let p2 = TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
110
|
+
let p3 = TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
111
|
+
// Tick 1: drain fiber D runs, takes msg1, calls handler → handler blocks on gate
|
|
112
|
+
let _ = await Promise.resolve()
|
|
113
|
+
// Gate not open yet — no messages fully processed
|
|
114
|
+
expect(processedCount.contents)->toBe(0)
|
|
115
|
+
// Open gate: subscriber can now process all pending messages
|
|
116
|
+
let _ = Deferred.succeed(gate, ())->Effect.runSync
|
|
117
|
+
// Await each publish promise; by the time all resolve, all 3 handlers have run
|
|
118
|
+
let _ = await p1
|
|
119
|
+
let _ = await p2
|
|
120
|
+
let _ = await p3
|
|
121
|
+
expect(processedCount.contents)->toBe(3)
|
|
122
|
+
})
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
describe("timing", () => {
|
|
126
|
+
testPromise("bounded mode completes within 3 microtask ticks", async () => {
|
|
127
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 10})
|
|
128
|
+
let delivered = ref(false)
|
|
129
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
130
|
+
delivered := true
|
|
131
|
+
})
|
|
132
|
+
let pubPromise = TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
133
|
+
// Not delivered synchronously
|
|
134
|
+
expect(delivered.contents)->toBe(false)
|
|
135
|
+
let _ = await Promise.resolve() // tick 1
|
|
136
|
+
let _ = await Promise.resolve() // tick 2
|
|
137
|
+
let _ = await Promise.resolve() // tick 3 (bounded adds 1 vs unbounded's 2)
|
|
138
|
+
let _ = await pubPromise
|
|
139
|
+
expect(delivered.contents)->toBe(true)
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe("reset", () => {
|
|
144
|
+
testPromise("reset shuts down bounded hubs cleanly", async () => {
|
|
145
|
+
module TestBus = LocalBus.MakeBounded({let capacity = 5})
|
|
146
|
+
let count = ref(0)
|
|
147
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
148
|
+
count := count.contents + 1
|
|
149
|
+
})
|
|
150
|
+
let _ = await TestBus.publishEvent("T", "svc", defaultMeta, JSON.Null)
|
|
151
|
+
expect(count.contents)->toBe(1)
|
|
152
|
+
// reset should not throw or hang
|
|
153
|
+
TestBus.reset()
|
|
154
|
+
expect(count.contents)->toBe(1) // count unchanged by reset itself
|
|
155
|
+
})
|
|
156
|
+
})
|
|
157
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Deferred from "effect/Deferred";
|
|
5
|
+
import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
|
|
6
|
+
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
7
|
+
|
|
8
|
+
TestRunner$ReventlessLocal.setup();
|
|
9
|
+
|
|
10
|
+
let defaultMeta = {
|
|
11
|
+
service: "test",
|
|
12
|
+
time: "",
|
|
13
|
+
ip: "",
|
|
14
|
+
user: "test",
|
|
15
|
+
msgId: "",
|
|
16
|
+
correlationId: ""
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
globalThis.describe("LocalBus bounded PubSub (Phase G)", () => {
|
|
20
|
+
globalThis.describe("basic delivery", () => {
|
|
21
|
+
globalThis.test("delivers a message to a subscriber in bounded mode", async () => {
|
|
22
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
23
|
+
capacity: 10
|
|
24
|
+
});
|
|
25
|
+
let delivered = {
|
|
26
|
+
contents: false
|
|
27
|
+
};
|
|
28
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
29
|
+
delivered.contents = true;
|
|
30
|
+
});
|
|
31
|
+
await TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
32
|
+
globalThis.expect(delivered.contents).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
globalThis.test("bounded mode with no subscribers returns immediately", async () => {
|
|
35
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
36
|
+
capacity: 5
|
|
37
|
+
});
|
|
38
|
+
return await TestBus.publishEvent("empty-topic", "svc", defaultMeta, null);
|
|
39
|
+
});
|
|
40
|
+
globalThis.test("passes service, meta, and json to the subscriber handler", async () => {
|
|
41
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
42
|
+
capacity: 10
|
|
43
|
+
});
|
|
44
|
+
let capturedService = {
|
|
45
|
+
contents: ""
|
|
46
|
+
};
|
|
47
|
+
let capturedJson = {
|
|
48
|
+
contents: null
|
|
49
|
+
};
|
|
50
|
+
TestBus.subscribeToEvents("T", async (svc, _meta, json) => {
|
|
51
|
+
capturedService.contents = svc;
|
|
52
|
+
capturedJson.contents = json;
|
|
53
|
+
});
|
|
54
|
+
let payload = JSON.parse("{\"x\":42}");
|
|
55
|
+
await TestBus.publishEvent("T", "my-service", defaultMeta, payload);
|
|
56
|
+
globalThis.expect(capturedService.contents).toBe("my-service");
|
|
57
|
+
globalThis.expect(capturedJson.contents).toEqual(payload);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
globalThis.describe("fan-out", () => {
|
|
61
|
+
globalThis.test("delivers to all subscribers on the same topic", async () => {
|
|
62
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
63
|
+
capacity: 10
|
|
64
|
+
});
|
|
65
|
+
let count = {
|
|
66
|
+
contents: 0
|
|
67
|
+
};
|
|
68
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
69
|
+
count.contents = count.contents + 1 | 0;
|
|
70
|
+
});
|
|
71
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
72
|
+
count.contents = count.contents + 1 | 0;
|
|
73
|
+
});
|
|
74
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
75
|
+
count.contents = count.contents + 1 | 0;
|
|
76
|
+
});
|
|
77
|
+
await TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
78
|
+
globalThis.expect(count.contents).toBe(3);
|
|
79
|
+
});
|
|
80
|
+
globalThis.test("topic isolation preserved in bounded mode", async () => {
|
|
81
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
82
|
+
capacity: 10
|
|
83
|
+
});
|
|
84
|
+
let countA = {
|
|
85
|
+
contents: 0
|
|
86
|
+
};
|
|
87
|
+
let countB = {
|
|
88
|
+
contents: 0
|
|
89
|
+
};
|
|
90
|
+
TestBus.subscribeToEvents("topic-a", async (param, param$1, param$2) => {
|
|
91
|
+
countA.contents = countA.contents + 1 | 0;
|
|
92
|
+
});
|
|
93
|
+
TestBus.subscribeToEvents("topic-b", async (param, param$1, param$2) => {
|
|
94
|
+
countB.contents = countB.contents + 1 | 0;
|
|
95
|
+
});
|
|
96
|
+
await TestBus.publishEvent("topic-a", "svc", defaultMeta, null);
|
|
97
|
+
globalThis.expect(countA.contents).toBe(1);
|
|
98
|
+
globalThis.expect(countB.contents).toBe(0);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
globalThis.describe("backpressure", () => {
|
|
102
|
+
globalThis.test("publisher suspends when subscriber queue is full (capacity=1)", async () => {
|
|
103
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
104
|
+
capacity: 1
|
|
105
|
+
});
|
|
106
|
+
let processedCount = {
|
|
107
|
+
contents: 0
|
|
108
|
+
};
|
|
109
|
+
let gate = Effect.runSync(Deferred.make());
|
|
110
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
111
|
+
await Effect.runPromise(Deferred.await(gate));
|
|
112
|
+
processedCount.contents = processedCount.contents + 1 | 0;
|
|
113
|
+
});
|
|
114
|
+
let p1 = TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
115
|
+
let p2 = TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
116
|
+
let p3 = TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
117
|
+
await Promise.resolve();
|
|
118
|
+
globalThis.expect(processedCount.contents).toBe(0);
|
|
119
|
+
Effect.runSync(Deferred.succeed(gate, undefined));
|
|
120
|
+
await p1;
|
|
121
|
+
await p2;
|
|
122
|
+
await p3;
|
|
123
|
+
globalThis.expect(processedCount.contents).toBe(3);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
globalThis.describe("timing", () => {
|
|
127
|
+
globalThis.test("bounded mode completes within 3 microtask ticks", async () => {
|
|
128
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
129
|
+
capacity: 10
|
|
130
|
+
});
|
|
131
|
+
let delivered = {
|
|
132
|
+
contents: false
|
|
133
|
+
};
|
|
134
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
135
|
+
delivered.contents = true;
|
|
136
|
+
});
|
|
137
|
+
let pubPromise = TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
138
|
+
globalThis.expect(delivered.contents).toBe(false);
|
|
139
|
+
await Promise.resolve();
|
|
140
|
+
await Promise.resolve();
|
|
141
|
+
await Promise.resolve();
|
|
142
|
+
await pubPromise;
|
|
143
|
+
globalThis.expect(delivered.contents).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
globalThis.describe("reset", () => {
|
|
147
|
+
globalThis.test("reset shuts down bounded hubs cleanly", async () => {
|
|
148
|
+
let TestBus = LocalBus$ReventlessLocal.MakeBounded({
|
|
149
|
+
capacity: 5
|
|
150
|
+
});
|
|
151
|
+
let count = {
|
|
152
|
+
contents: 0
|
|
153
|
+
};
|
|
154
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
155
|
+
count.contents = count.contents + 1 | 0;
|
|
156
|
+
});
|
|
157
|
+
await TestBus.publishEvent("T", "svc", defaultMeta, null);
|
|
158
|
+
globalThis.expect(count.contents).toBe(1);
|
|
159
|
+
TestBus.reset();
|
|
160
|
+
globalThis.expect(count.contents).toBe(1);
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
export {
|
|
166
|
+
defaultMeta,
|
|
167
|
+
}
|
|
168
|
+
/* Not a pure module */
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Tests for the PubSub-based LocalBus implementation (Phase F).
|
|
2
|
+
// Verifies the 2-tick timing guarantee and multi-subscriber fan-out.
|
|
3
|
+
//
|
|
4
|
+
// These tests lock in two invariants of the PubSub refactor:
|
|
5
|
+
// 1. publishEvent still resolves in exactly 2 microtask ticks (unbounded mode).
|
|
6
|
+
// 2. A single publishEvent call reaches all subscribers on the same topic.
|
|
7
|
+
|
|
8
|
+
open JestGlobals
|
|
9
|
+
|
|
10
|
+
let _ = TestRunner.setup()
|
|
11
|
+
|
|
12
|
+
let defaultMeta: Reventless.Message.meta = {
|
|
13
|
+
service: "test",
|
|
14
|
+
time: "",
|
|
15
|
+
ip: "",
|
|
16
|
+
user: "test",
|
|
17
|
+
msgId: "",
|
|
18
|
+
correlationId: "",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
describe("LocalBus PubSub (Phase F)", () => {
|
|
22
|
+
describe("publishEvent timing", () => {
|
|
23
|
+
testPromise("resolves after exactly 2 microtask ticks", async () => {
|
|
24
|
+
module TestBus = LocalBus.Make()
|
|
25
|
+
let delivered = ref(false)
|
|
26
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
27
|
+
delivered := true
|
|
28
|
+
})
|
|
29
|
+
// Start publishEvent without awaiting — drive manually tick by tick.
|
|
30
|
+
let pubPromise = TestBus.publishEvent("T", "svc", defaultMeta, JSON.parseOrThrow("{}"))
|
|
31
|
+
// Tick 0: publish is synchronous; drain fiber hasn't run yet.
|
|
32
|
+
expect(delivered.contents)->toBe(false)
|
|
33
|
+
// Tick 1: Effect scheduler runs drain fiber; handler sets delivered.
|
|
34
|
+
let _ = await Promise.resolve()
|
|
35
|
+
// Tick 2: allDone Deferred resolves; pubPromise completes.
|
|
36
|
+
let _ = await Promise.resolve()
|
|
37
|
+
let _ = await pubPromise
|
|
38
|
+
expect(delivered.contents)->toBe(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
testPromise("publishEvent with no subscribers returns immediately", async () => {
|
|
42
|
+
module TestBus = LocalBus.Make()
|
|
43
|
+
// No subscribers registered — should not hang.
|
|
44
|
+
await TestBus.publishEvent("empty-topic", "svc", defaultMeta, JSON.Null)
|
|
45
|
+
// If we reach here the test passes (no timeout/hang).
|
|
46
|
+
})
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
describe("fan-out", () => {
|
|
50
|
+
testPromise("delivers to all subscribers on the same topic", async () => {
|
|
51
|
+
module TestBus = LocalBus.Make()
|
|
52
|
+
let count = ref(0)
|
|
53
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
54
|
+
count := count.contents + 1
|
|
55
|
+
})
|
|
56
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
57
|
+
count := count.contents + 1
|
|
58
|
+
})
|
|
59
|
+
TestBus.subscribeToEvents("T", async (_, _, _) => {
|
|
60
|
+
count := count.contents + 1
|
|
61
|
+
})
|
|
62
|
+
let _ = await TestBus.publishEvent("T", "svc", defaultMeta, JSON.parseOrThrow("{}"))
|
|
63
|
+
expect(count.contents)->toBe(3)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
testPromise("publishing to topic-a does not reach topic-b subscribers", async () => {
|
|
67
|
+
module TestBus = LocalBus.Make()
|
|
68
|
+
let countA = ref(0)
|
|
69
|
+
let countB = ref(0)
|
|
70
|
+
TestBus.subscribeToEvents("topic-a", async (_, _, _) => {
|
|
71
|
+
countA := countA.contents + 1
|
|
72
|
+
})
|
|
73
|
+
TestBus.subscribeToEvents("topic-b", async (_, _, _) => {
|
|
74
|
+
countB := countB.contents + 1
|
|
75
|
+
})
|
|
76
|
+
let _ = await TestBus.publishEvent("topic-a", "svc", defaultMeta, JSON.Null)
|
|
77
|
+
expect(countA.contents)->toBe(1)
|
|
78
|
+
expect(countB.contents)->toBe(0)
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as LocalBus$ReventlessLocal from "../../src/adapter/LocalBus.res.mjs";
|
|
4
|
+
import * as TestRunner$ReventlessLocal from "../../src/test/TestRunner.res.mjs";
|
|
5
|
+
|
|
6
|
+
TestRunner$ReventlessLocal.setup();
|
|
7
|
+
|
|
8
|
+
let defaultMeta = {
|
|
9
|
+
service: "test",
|
|
10
|
+
time: "",
|
|
11
|
+
ip: "",
|
|
12
|
+
user: "test",
|
|
13
|
+
msgId: "",
|
|
14
|
+
correlationId: ""
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
globalThis.describe("LocalBus PubSub (Phase F)", () => {
|
|
18
|
+
globalThis.describe("publishEvent timing", () => {
|
|
19
|
+
globalThis.test("resolves after exactly 2 microtask ticks", async () => {
|
|
20
|
+
let TestBus = LocalBus$ReventlessLocal.Make({});
|
|
21
|
+
let delivered = {
|
|
22
|
+
contents: false
|
|
23
|
+
};
|
|
24
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
25
|
+
delivered.contents = true;
|
|
26
|
+
});
|
|
27
|
+
let pubPromise = TestBus.publishEvent("T", "svc", defaultMeta, JSON.parse("{}"));
|
|
28
|
+
globalThis.expect(delivered.contents).toBe(false);
|
|
29
|
+
await Promise.resolve();
|
|
30
|
+
await Promise.resolve();
|
|
31
|
+
await pubPromise;
|
|
32
|
+
globalThis.expect(delivered.contents).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
globalThis.test("publishEvent with no subscribers returns immediately", async () => {
|
|
35
|
+
let TestBus = LocalBus$ReventlessLocal.Make({});
|
|
36
|
+
return await TestBus.publishEvent("empty-topic", "svc", defaultMeta, null);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
globalThis.describe("fan-out", () => {
|
|
40
|
+
globalThis.test("delivers to all subscribers on the same topic", async () => {
|
|
41
|
+
let TestBus = LocalBus$ReventlessLocal.Make({});
|
|
42
|
+
let count = {
|
|
43
|
+
contents: 0
|
|
44
|
+
};
|
|
45
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
46
|
+
count.contents = count.contents + 1 | 0;
|
|
47
|
+
});
|
|
48
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
49
|
+
count.contents = count.contents + 1 | 0;
|
|
50
|
+
});
|
|
51
|
+
TestBus.subscribeToEvents("T", async (param, param$1, param$2) => {
|
|
52
|
+
count.contents = count.contents + 1 | 0;
|
|
53
|
+
});
|
|
54
|
+
await TestBus.publishEvent("T", "svc", defaultMeta, JSON.parse("{}"));
|
|
55
|
+
globalThis.expect(count.contents).toBe(3);
|
|
56
|
+
});
|
|
57
|
+
globalThis.test("publishing to topic-a does not reach topic-b subscribers", async () => {
|
|
58
|
+
let TestBus = LocalBus$ReventlessLocal.Make({});
|
|
59
|
+
let countA = {
|
|
60
|
+
contents: 0
|
|
61
|
+
};
|
|
62
|
+
let countB = {
|
|
63
|
+
contents: 0
|
|
64
|
+
};
|
|
65
|
+
TestBus.subscribeToEvents("topic-a", async (param, param$1, param$2) => {
|
|
66
|
+
countA.contents = countA.contents + 1 | 0;
|
|
67
|
+
});
|
|
68
|
+
TestBus.subscribeToEvents("topic-b", async (param, param$1, param$2) => {
|
|
69
|
+
countB.contents = countB.contents + 1 | 0;
|
|
70
|
+
});
|
|
71
|
+
await TestBus.publishEvent("topic-a", "svc", defaultMeta, null);
|
|
72
|
+
globalThis.expect(countA.contents).toBe(1);
|
|
73
|
+
globalThis.expect(countB.contents).toBe(0);
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
export {
|
|
79
|
+
defaultMeta,
|
|
80
|
+
}
|
|
81
|
+
/* Not a pure module */
|