@cratis/arc 22.0.2 → 22.2.0
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/dist/cjs/queries/ObservableQueryFor.js +6 -1
- package/dist/cjs/queries/ObservableQueryFor.js.map +1 -1
- package/dist/cjs/queries/QueryInstanceCache.js +6 -6
- package/dist/cjs/queries/QueryInstanceCache.js.map +1 -1
- package/dist/cjs/queries/QueryResult.js +23 -4
- package/dist/cjs/queries/QueryResult.js.map +1 -1
- package/dist/cjs/queries/QueryResultWithState.js +20 -7
- package/dist/cjs/queries/QueryResultWithState.js.map +1 -1
- package/dist/cjs/queries/ServerSentEventHubConnection.js +120 -58
- package/dist/cjs/queries/ServerSentEventHubConnection.js.map +1 -1
- package/dist/cjs/queries/WebSocketHubConnection.js +121 -51
- package/dist/cjs/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/IQueryResult.d.ts +1 -0
- package/dist/esm/queries/IQueryResult.d.ts.map +1 -1
- package/dist/esm/queries/ObservableQueryFor.d.ts.map +1 -1
- package/dist/esm/queries/ObservableQueryFor.js +6 -1
- package/dist/esm/queries/ObservableQueryFor.js.map +1 -1
- package/dist/esm/queries/QueryInstanceCache.d.ts +1 -1
- package/dist/esm/queries/QueryInstanceCache.d.ts.map +1 -1
- package/dist/esm/queries/QueryInstanceCache.js +6 -6
- package/dist/esm/queries/QueryInstanceCache.js.map +1 -1
- package/dist/esm/queries/QueryResult.d.ts +2 -0
- package/dist/esm/queries/QueryResult.d.ts.map +1 -1
- package/dist/esm/queries/QueryResult.js +23 -4
- package/dist/esm/queries/QueryResult.js.map +1 -1
- package/dist/esm/queries/QueryResultWithState.d.ts +3 -3
- package/dist/esm/queries/QueryResultWithState.d.ts.map +1 -1
- package/dist/esm/queries/QueryResultWithState.js +20 -7
- package/dist/esm/queries/QueryResultWithState.js.map +1 -1
- package/dist/esm/queries/ServerSentEventHubConnection.d.ts +11 -4
- package/dist/esm/queries/ServerSentEventHubConnection.d.ts.map +1 -1
- package/dist/esm/queries/ServerSentEventHubConnection.js +120 -58
- package/dist/esm/queries/ServerSentEventHubConnection.js.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.d.ts +13 -2
- package/dist/esm/queries/WebSocketHubConnection.d.ts.map +1 -1
- package/dist/esm/queries/WebSocketHubConnection.js +121 -51
- package/dist/esm/queries/WebSocketHubConnection.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts +2 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.d.ts.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js +34 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.js.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js +26 -2
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js +60 -3
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.js.map +1 -1
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js +83 -0
- package/dist/esm/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.js.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts +2 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js +22 -0
- package/dist/esm/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.js.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts +2 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js +17 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.js.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js +16 -0
- package/dist/esm/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.js.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts +2 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js +19 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.js.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts +2 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.d.ts.map +1 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js +19 -0
- package/dist/esm/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts +7 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js +12 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts +2 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.d.ts.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js +15 -4
- package/dist/esm/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js +44 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js +74 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js +74 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js +59 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.js +58 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js +6 -3
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +23 -6
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js +20 -5
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js +20 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js +68 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js +38 -13
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.js.map +1 -1
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts +2 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.d.ts.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js +54 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.js.map +1 -0
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js +18 -4
- package/dist/esm/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js +22 -5
- package/dist/esm/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js +108 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js +33 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js +26 -4
- package/dist/esm/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js +62 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js +18 -5
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js +56 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.js.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js +16 -3
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.js.map +1 -1
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts +2 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.d.ts.map +1 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js +33 -0
- package/dist/esm/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.js.map +1 -0
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/queries/IQueryResult.ts +10 -0
- package/queries/ObservableQueryFor.ts +44 -16
- package/queries/QueryInstanceCache.ts +42 -19
- package/queries/QueryResult.ts +145 -79
- package/queries/QueryResultWithState.ts +32 -9
- package/queries/ServerSentEventHubConnection.ts +270 -83
- package/queries/WebSocketHubConnection.ts +188 -56
- package/queries/for_ObservableQueryFor/when_performing/with_a_not_ready_result.ts +46 -0
- package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_and_sse_transport.ts +91 -57
- package/queries/for_ObservableQueryFor/when_subscribing/with_direct_mode_disabled_and_sse_transport.ts +125 -53
- package/queries/for_ObservableQueryFor/when_subscribing/with_web_socket_hub_payload_without_readiness.ts +107 -0
- package/queries/for_QueryInstanceCache/when_setting_last_result/with_only_readiness_changed.ts +63 -0
- package/queries/for_QueryResult/when_constructing_from_a_server_result/with_readiness_false.ts +25 -0
- package/queries/for_QueryResult/when_constructing_from_a_server_result/without_readiness.ts +24 -0
- package/queries/for_QueryResultWithState/when_creating_from_query_result/with_readiness_false.ts +28 -0
- package/queries/for_QueryResultWithState/when_creating_from_query_result/without_readiness.ts +25 -0
- package/queries/for_ServerSentEventHubConnection/given/a_deferred_promise.ts +15 -0
- package/queries/for_ServerSentEventHubConnection/given/a_server_sent_event_hub_connection.ts +18 -7
- package/queries/for_ServerSentEventHubConnection/when_a_retired_event_source_fires/ignores_connected_and_error_callbacks.ts +59 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_replaced.ts +113 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_query_is_unsubscribed.ts +109 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_is_rejected.ts +81 -0
- package/queries/for_ServerSentEventHubConnection/when_connection_changes_while_subscribe_is_in_flight/and_the_stale_request_returns_non_ok.ts +83 -0
- package/queries/for_ServerSentEventHubConnection/when_disposing/cleans_up_resources.ts +30 -21
- package/queries/for_ServerSentEventHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +56 -32
- package/queries/for_ServerSentEventHubConnection/when_reconnecting/after_error.ts +23 -6
- package/queries/for_ServerSentEventHubConnection/when_reconnecting/and_an_immediate_open_wins_the_backoff_race.ts +41 -0
- package/queries/for_ServerSentEventHubConnection/when_replacing_a_subscription/ignores_messages_from_the_retired_generation.ts +92 -0
- package/queries/for_ServerSentEventHubConnection/when_subscribing/sends_subscribe_post.ts +69 -30
- package/queries/for_ServerSentEventHubConnection/when_subscription_revisions_are_not_advertised/uses_legacy_protocol.ts +69 -0
- package/queries/for_ServerSentEventHubConnection/when_unsubscribing/the_only_query.ts +43 -24
- package/queries/for_WebSocketHubConnection/when_receiving_unauthorized/notifies_subscriber_and_removes_subscription.ts +53 -29
- package/queries/for_WebSocketHubConnection/when_reconnecting/ignores_a_pending_reconnect_and_stale_socket_callbacks.ts +131 -0
- package/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.ts +39 -0
- package/queries/for_WebSocketHubConnection/when_reconnecting/resubscribes_all_queries.ts +72 -36
- package/queries/for_WebSocketHubConnection/when_replacing_a_subscription/ignores_stale_result_unauthorized_and_error.ts +88 -0
- package/queries/for_WebSocketHubConnection/when_subscribing/to_a_query.ts +54 -27
- package/queries/for_WebSocketHubConnection/when_subscription_revisions_are_supported/upgrades_active_subscriptions_to_strict_mode.ts +72 -0
- package/queries/for_WebSocketHubConnection/when_unsubscribing/the_only_query.ts +43 -18
- package/queries/for_WebSocketHubConnection/when_unsubscribing/with_revision_support_not_advertised.ts +44 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { a_deferred_promise } from '../given/a_deferred_promise';
|
|
6
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
7
|
+
import { given } from '../../../given';
|
|
8
|
+
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
9
|
+
|
|
10
|
+
describe(
|
|
11
|
+
'when a query is replaced while its subscribe request is in flight and the connection changes',
|
|
12
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
13
|
+
const queryId = 'q1';
|
|
14
|
+
const replacementRequest = { queryName: 'ReplacementQuery' };
|
|
15
|
+
let clock: sinon.SinonFakeTimers;
|
|
16
|
+
let originalCallback: sinon.SinonStub;
|
|
17
|
+
let replacementCallback: sinon.SinonStub;
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
context.setup();
|
|
21
|
+
clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
|
|
22
|
+
sinon.stub(console, 'warn');
|
|
23
|
+
const firstSubscribe = new a_deferred_promise<{
|
|
24
|
+
ok: boolean;
|
|
25
|
+
status: number;
|
|
26
|
+
}>();
|
|
27
|
+
context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
|
|
28
|
+
originalCallback = sinon.stub();
|
|
29
|
+
replacementCallback = sinon.stub();
|
|
30
|
+
|
|
31
|
+
context.connection.subscribe(
|
|
32
|
+
queryId,
|
|
33
|
+
{ queryName: 'OriginalQuery' },
|
|
34
|
+
originalCallback,
|
|
35
|
+
);
|
|
36
|
+
context.simulateOpen();
|
|
37
|
+
context.simulateMessage({
|
|
38
|
+
type: HubMessageType.Connected,
|
|
39
|
+
payload: 'connection-a',
|
|
40
|
+
supportsSubscriptionRevisions: true,
|
|
41
|
+
});
|
|
42
|
+
context.connection.subscribe(
|
|
43
|
+
queryId,
|
|
44
|
+
replacementRequest,
|
|
45
|
+
replacementCallback,
|
|
46
|
+
);
|
|
47
|
+
context.simulateMessage({
|
|
48
|
+
type: HubMessageType.Connected,
|
|
49
|
+
payload: 'connection-b',
|
|
50
|
+
supportsSubscriptionRevisions: true,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
firstSubscribe.resolve({ ok: false, status: 404 });
|
|
54
|
+
await Promise.resolve();
|
|
55
|
+
clock.tick(1000);
|
|
56
|
+
await Promise.resolve();
|
|
57
|
+
context.simulateMessage({
|
|
58
|
+
type: HubMessageType.QueryResult,
|
|
59
|
+
queryId,
|
|
60
|
+
revision: getSubscribeBody(2).revision,
|
|
61
|
+
payload: { isSuccess: true },
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
afterEach(() => {
|
|
66
|
+
clock.restore();
|
|
67
|
+
sinon.restore();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should send only the replacement on connection B', () => {
|
|
71
|
+
const body = getSubscribeBody(2);
|
|
72
|
+
body.connectionId.should.equal('connection-b');
|
|
73
|
+
body.queryId.should.equal(queryId);
|
|
74
|
+
body.request.should.deep.equal(replacementRequest);
|
|
75
|
+
body.revision.should.be.greaterThan(0);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should not retry the obsolete subscription', () =>
|
|
79
|
+
context.fetchStub.callCount.should.equal(3));
|
|
80
|
+
|
|
81
|
+
it('should notify only the replacement callback', () => {
|
|
82
|
+
originalCallback.called.should.equal(false);
|
|
83
|
+
replacementCallback.calledOnce.should.equal(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should not reconnect', () =>
|
|
87
|
+
(context.policy.schedule as sinon.SinonStub).called.should.be.false);
|
|
88
|
+
|
|
89
|
+
it('should retain one active subscription', () =>
|
|
90
|
+
context.connection.queryCount.should.equal(1));
|
|
91
|
+
|
|
92
|
+
function getSubscribeBody(callIndex: number): {
|
|
93
|
+
connectionId: string;
|
|
94
|
+
queryId: string;
|
|
95
|
+
request: object;
|
|
96
|
+
revision: number;
|
|
97
|
+
} {
|
|
98
|
+
const rawBody = context.fetchStub.getCall(callIndex).args[1].body as string;
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(rawBody) as {
|
|
101
|
+
connectionId: string;
|
|
102
|
+
queryId: string;
|
|
103
|
+
request: object;
|
|
104
|
+
revision: number;
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
108
|
+
cause: error,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}),
|
|
113
|
+
);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { a_deferred_promise } from '../given/a_deferred_promise';
|
|
6
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
7
|
+
import { given } from '../../../given';
|
|
8
|
+
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
9
|
+
|
|
10
|
+
const subscribeUrl = 'http://localhost/.cratis/queries/sse/subscribe';
|
|
11
|
+
|
|
12
|
+
describe(
|
|
13
|
+
'when a query is unsubscribed while its subscribe request is in flight and the connection changes',
|
|
14
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
15
|
+
let clock: sinon.SinonFakeTimers;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
context.setup();
|
|
19
|
+
clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
|
|
20
|
+
sinon.stub(console, 'warn');
|
|
21
|
+
const firstSubscribe = new a_deferred_promise<{
|
|
22
|
+
ok: boolean;
|
|
23
|
+
status: number;
|
|
24
|
+
}>();
|
|
25
|
+
context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
|
|
26
|
+
|
|
27
|
+
context.connection.subscribe(
|
|
28
|
+
'removed-query',
|
|
29
|
+
{ queryName: 'RemovedQuery' },
|
|
30
|
+
sinon.stub(),
|
|
31
|
+
);
|
|
32
|
+
context.simulateOpen();
|
|
33
|
+
context.simulateMessage({
|
|
34
|
+
type: HubMessageType.Connected,
|
|
35
|
+
payload: 'connection-a',
|
|
36
|
+
supportsSubscriptionRevisions: true,
|
|
37
|
+
});
|
|
38
|
+
context.connection.subscribe(
|
|
39
|
+
'active-query',
|
|
40
|
+
{ queryName: 'ActiveQuery' },
|
|
41
|
+
sinon.stub(),
|
|
42
|
+
);
|
|
43
|
+
context.connection.unsubscribe('removed-query');
|
|
44
|
+
context.simulateMessage({
|
|
45
|
+
type: HubMessageType.Connected,
|
|
46
|
+
payload: 'connection-b',
|
|
47
|
+
supportsSubscriptionRevisions: true,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
firstSubscribe.resolve({ ok: false, status: 404 });
|
|
51
|
+
await Promise.resolve();
|
|
52
|
+
clock.tick(1000);
|
|
53
|
+
await Promise.resolve();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
clock.restore();
|
|
58
|
+
sinon.restore();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should not subscribe the removed query on connection B', () => {
|
|
62
|
+
const removedQueryCalls = context.fetchStub
|
|
63
|
+
.getCalls()
|
|
64
|
+
.filter((call) => call.args[0] === subscribeUrl)
|
|
65
|
+
.filter((call) =>
|
|
66
|
+
(call.args[1].body as string).includes('"queryId":"removed-query"'),
|
|
67
|
+
);
|
|
68
|
+
removedQueryCalls.length.should.equal(1);
|
|
69
|
+
const body = parseBody(removedQueryCalls[0].args[1].body as string);
|
|
70
|
+
body.connectionId.should.equal('connection-a');
|
|
71
|
+
body.queryId.should.equal('removed-query');
|
|
72
|
+
body.request.should.deep.equal({ queryName: 'RemovedQuery' });
|
|
73
|
+
body.revision.should.be.greaterThan(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should subscribe the remaining query on connection B', () => {
|
|
77
|
+
const activeQueryBodies = context.fetchStub
|
|
78
|
+
.getCalls()
|
|
79
|
+
.filter((call) => call.args[0] === subscribeUrl)
|
|
80
|
+
.map((call) => parseBody(call.args[1].body as string))
|
|
81
|
+
.filter((body) => body.queryId === 'active-query');
|
|
82
|
+
activeQueryBodies
|
|
83
|
+
.map((body) => body.connectionId)
|
|
84
|
+
.should.deep.equal(['connection-a', 'connection-b']);
|
|
85
|
+
activeQueryBodies[0].revision.should.equal(activeQueryBodies[1].revision);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should not reconnect', () =>
|
|
89
|
+
(context.policy.schedule as sinon.SinonStub).called.should.be.false);
|
|
90
|
+
|
|
91
|
+
it('should retain only the remaining subscription', () =>
|
|
92
|
+
context.connection.queryCount.should.equal(1));
|
|
93
|
+
|
|
94
|
+
function parseBody(rawBody: string): {
|
|
95
|
+
connectionId: string;
|
|
96
|
+
queryId: string;
|
|
97
|
+
request: { queryName: string };
|
|
98
|
+
revision: number;
|
|
99
|
+
} {
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
104
|
+
cause: error,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}),
|
|
109
|
+
);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { a_deferred_promise } from '../given/a_deferred_promise';
|
|
6
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
7
|
+
import { given } from '../../../given';
|
|
8
|
+
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
9
|
+
|
|
10
|
+
describe(
|
|
11
|
+
'when the connection changes while a subscribe request is in flight and the stale request is rejected',
|
|
12
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
13
|
+
const queryId = 'q1';
|
|
14
|
+
const request = { queryName: 'MyQuery' };
|
|
15
|
+
let clock: sinon.SinonFakeTimers;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
context.setup();
|
|
19
|
+
clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
|
|
20
|
+
sinon.stub(console, 'warn');
|
|
21
|
+
const firstSubscribe = new a_deferred_promise<{ ok: boolean }>();
|
|
22
|
+
context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
|
|
23
|
+
|
|
24
|
+
context.connection.subscribe(queryId, request, sinon.stub());
|
|
25
|
+
context.simulateOpen();
|
|
26
|
+
context.simulateMessage({
|
|
27
|
+
type: HubMessageType.Connected,
|
|
28
|
+
payload: 'connection-a',
|
|
29
|
+
supportsSubscriptionRevisions: true,
|
|
30
|
+
});
|
|
31
|
+
context.simulateMessage({
|
|
32
|
+
type: HubMessageType.Connected,
|
|
33
|
+
payload: 'connection-b',
|
|
34
|
+
supportsSubscriptionRevisions: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
firstSubscribe.reject(new Error('Connection A failed'));
|
|
38
|
+
await Promise.resolve();
|
|
39
|
+
await Promise.resolve();
|
|
40
|
+
clock.tick(1000);
|
|
41
|
+
await Promise.resolve();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
afterEach(() => {
|
|
45
|
+
clock.restore();
|
|
46
|
+
sinon.restore();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should send the active subscription on connection B', () => {
|
|
50
|
+
const body = parseBody(context.fetchStub.getCall(1).args[1].body as string);
|
|
51
|
+
body.connectionId.should.equal('connection-b');
|
|
52
|
+
body.queryId.should.equal(queryId);
|
|
53
|
+
body.request.should.deep.equal(request);
|
|
54
|
+
body.revision.should.be.greaterThan(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should not retry the obsolete connection A request', () =>
|
|
58
|
+
context.fetchStub.callCount.should.equal(2));
|
|
59
|
+
|
|
60
|
+
it('should not reconnect', () =>
|
|
61
|
+
(context.policy.schedule as sinon.SinonStub).called.should.be.false);
|
|
62
|
+
|
|
63
|
+
it('should retain the active subscription', () =>
|
|
64
|
+
context.connection.queryCount.should.equal(1));
|
|
65
|
+
|
|
66
|
+
function parseBody(rawBody: string): {
|
|
67
|
+
connectionId: string;
|
|
68
|
+
queryId: string;
|
|
69
|
+
request: object;
|
|
70
|
+
revision: number;
|
|
71
|
+
} {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
76
|
+
cause: error,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { a_deferred_promise } from '../given/a_deferred_promise';
|
|
6
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
7
|
+
import { given } from '../../../given';
|
|
8
|
+
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
9
|
+
|
|
10
|
+
describe(
|
|
11
|
+
'when the connection changes while a subscribe request is in flight and the stale request returns non-OK',
|
|
12
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
13
|
+
const queryId = 'q1';
|
|
14
|
+
const request = { queryName: 'MyQuery' };
|
|
15
|
+
let clock: sinon.SinonFakeTimers;
|
|
16
|
+
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
context.setup();
|
|
19
|
+
clock = sinon.useFakeTimers({ toFake: ['setTimeout'] });
|
|
20
|
+
sinon.stub(console, 'warn');
|
|
21
|
+
const firstSubscribe = new a_deferred_promise<{
|
|
22
|
+
ok: boolean;
|
|
23
|
+
status: number;
|
|
24
|
+
}>();
|
|
25
|
+
context.fetchStub.onFirstCall().returns(firstSubscribe.promise);
|
|
26
|
+
|
|
27
|
+
context.connection.subscribe(queryId, request, sinon.stub());
|
|
28
|
+
context.simulateOpen();
|
|
29
|
+
context.simulateMessage({
|
|
30
|
+
type: HubMessageType.Connected,
|
|
31
|
+
payload: 'connection-a',
|
|
32
|
+
supportsSubscriptionRevisions: true,
|
|
33
|
+
});
|
|
34
|
+
context.simulateMessage({
|
|
35
|
+
type: HubMessageType.Connected,
|
|
36
|
+
payload: 'connection-b',
|
|
37
|
+
supportsSubscriptionRevisions: true,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
firstSubscribe.resolve({ ok: false, status: 404 });
|
|
41
|
+
await Promise.resolve();
|
|
42
|
+
clock.tick(1000);
|
|
43
|
+
await Promise.resolve();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
clock.restore();
|
|
48
|
+
sinon.restore();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should send the active subscription on connection B', () => {
|
|
52
|
+
const body = parseBody(context.fetchStub.getCall(1).args[1].body as string);
|
|
53
|
+
body.connectionId.should.equal('connection-b');
|
|
54
|
+
body.queryId.should.equal(queryId);
|
|
55
|
+
body.request.should.deep.equal(request);
|
|
56
|
+
body.revision.should.be.greaterThan(0);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should not retry the obsolete connection A request', () =>
|
|
60
|
+
context.fetchStub.callCount.should.equal(2));
|
|
61
|
+
|
|
62
|
+
it('should not reconnect', () =>
|
|
63
|
+
(context.policy.schedule as sinon.SinonStub).called.should.be.false);
|
|
64
|
+
|
|
65
|
+
it('should retain the active subscription', () =>
|
|
66
|
+
context.connection.queryCount.should.equal(1));
|
|
67
|
+
|
|
68
|
+
function parseBody(rawBody: string): {
|
|
69
|
+
connectionId: string;
|
|
70
|
+
queryId: string;
|
|
71
|
+
request: object;
|
|
72
|
+
revision: number;
|
|
73
|
+
} {
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(rawBody) as ReturnType<typeof parseBody>;
|
|
76
|
+
} catch (error) {
|
|
77
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
78
|
+
cause: error,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
@@ -6,29 +6,38 @@ import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event
|
|
|
6
6
|
import { given } from '../../../given';
|
|
7
7
|
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
8
8
|
|
|
9
|
-
describe(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
describe(
|
|
10
|
+
'when disposing the hub connection',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
context.setup();
|
|
14
|
+
context.connection.subscribe('q1', { queryName: 'MyQuery' }, sinon.stub());
|
|
15
|
+
context.simulateOpen();
|
|
16
|
+
context.simulateMessage({
|
|
17
|
+
type: HubMessageType.Connected,
|
|
18
|
+
payload: 'conn-1',
|
|
19
|
+
});
|
|
20
|
+
context.connection.dispose();
|
|
21
|
+
});
|
|
17
22
|
|
|
18
|
-
|
|
23
|
+
afterEach(() => sinon.restore());
|
|
19
24
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
it('should close the EventSource', () =>
|
|
26
|
+
context.fakeEventSource.close.calledOnce.should.be.true);
|
|
27
|
+
it('should cancel the reconnect policy', () =>
|
|
28
|
+
(context.policy.cancel as sinon.SinonStub).called.should.equal(true));
|
|
29
|
+
it('should report zero active subscriptions', () =>
|
|
30
|
+
context.connection.queryCount.should.equal(0));
|
|
23
31
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
describe('when an error event fires after dispose', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
(context.policy.schedule as sinon.SinonStub).reset();
|
|
35
|
+
context.simulateError();
|
|
36
|
+
});
|
|
29
37
|
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
it('should not schedule a reconnect', () => {
|
|
39
|
+
(context.policy.schedule as sinon.SinonStub).callCount.should.equal(0);
|
|
40
|
+
});
|
|
32
41
|
});
|
|
33
|
-
})
|
|
34
|
-
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
@@ -7,45 +7,69 @@ import { given } from '../../../given';
|
|
|
7
7
|
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
8
8
|
import { QueryResult } from '../../QueryResult';
|
|
9
9
|
|
|
10
|
-
describe(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
describe(
|
|
11
|
+
'when receiving unauthorized for a subscribed query',
|
|
12
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
13
|
+
let callback: sinon.SinonStub;
|
|
14
|
+
let receivedResult: QueryResult<unknown> | undefined;
|
|
15
|
+
const queryId = 'q-auth-1';
|
|
16
|
+
const connectionId = 'conn-123';
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
context.setup();
|
|
20
|
+
callback = sinon.stub();
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
context.connection.subscribe(queryId, { queryName: 'SecureQuery' }, callback);
|
|
23
|
+
context.simulateOpen();
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
// Server sends Connected message with the connection ID.
|
|
26
|
+
context.simulateMessage({
|
|
27
|
+
type: HubMessageType.Connected,
|
|
28
|
+
payload: connectionId,
|
|
29
|
+
supportsSubscriptionRevisions: true,
|
|
30
|
+
});
|
|
31
|
+
const body = getSubscribeBody();
|
|
25
32
|
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
// Server replies with Unauthorized for this exact subscription.
|
|
34
|
+
context.simulateMessage({
|
|
35
|
+
type: HubMessageType.Unauthorized,
|
|
36
|
+
queryId,
|
|
37
|
+
revision: body.revision,
|
|
38
|
+
});
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
40
|
+
receivedResult = callback.firstCall?.args[0] as
|
|
41
|
+
| QueryResult<unknown>
|
|
42
|
+
| undefined;
|
|
43
|
+
});
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
sinon.restore();
|
|
47
|
+
});
|
|
35
48
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
function getSubscribeBody(): { revision: number } {
|
|
50
|
+
try {
|
|
51
|
+
return JSON.parse(context.fetchStub.firstCall.args[1].body as string) as {
|
|
52
|
+
revision: number;
|
|
53
|
+
};
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new Error('Expected a valid subscribe body', { cause: error });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
it('should invoke the subscriber callback', () => {
|
|
60
|
+
callback.calledOnce.should.equal(true);
|
|
61
|
+
});
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
63
|
+
it('should report isAuthorized as false', () => {
|
|
64
|
+
receivedResult!.isAuthorized.should.equal(false);
|
|
65
|
+
});
|
|
47
66
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
67
|
+
it('should report isSuccess as false', () => {
|
|
68
|
+
receivedResult!.isSuccess.should.equal(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should remove the subscription', () => {
|
|
72
|
+
context.connection.queryCount.should.equal(0);
|
|
73
|
+
});
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
@@ -14,7 +14,11 @@ describe('when connection errors', given(a_server_sent_event_hub_connection, con
|
|
|
14
14
|
context.setup();
|
|
15
15
|
context.connection.subscribe(queryId, { queryName: 'MyQuery' }, sinon.stub());
|
|
16
16
|
context.simulateOpen();
|
|
17
|
-
context.simulateMessage({
|
|
17
|
+
context.simulateMessage({
|
|
18
|
+
type: HubMessageType.Connected,
|
|
19
|
+
payload: 'conn-1',
|
|
20
|
+
supportsSubscriptionRevisions: true,
|
|
21
|
+
});
|
|
18
22
|
context.simulateError();
|
|
19
23
|
});
|
|
20
24
|
|
|
@@ -31,15 +35,14 @@ describe('when connection errors', given(a_server_sent_event_hub_connection, con
|
|
|
31
35
|
|
|
32
36
|
beforeEach(() => {
|
|
33
37
|
newEventSourceCreated = false;
|
|
34
|
-
// Replace the global EventSource with a stub that records construction
|
|
38
|
+
// Replace the global EventSource with a stub that records construction.
|
|
39
|
+
// SAFETY: This test double supplies the EventSource constructor and static ready-state shape used by the client.
|
|
35
40
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
41
|
const FakeClass = function (this: any) {
|
|
37
42
|
newEventSourceCreated = true;
|
|
38
43
|
return context.fakeEventSource;
|
|
39
44
|
} as unknown as new (url: string) => EventSource;
|
|
40
|
-
(FakeClass
|
|
41
|
-
(FakeClass as unknown as Record<string, unknown>)['OPEN'] = 1;
|
|
42
|
-
(FakeClass as unknown as Record<string, unknown>)['CLOSED'] = 2;
|
|
45
|
+
Object.assign(FakeClass, { CONNECTING: 0, OPEN: 1, CLOSED: 2 });
|
|
43
46
|
(globalThis as Record<string, unknown>)['EventSource'] = FakeClass;
|
|
44
47
|
|
|
45
48
|
const reconnectCallback = (context.policy.schedule as sinon.SinonStub).getCall(0).args[0] as ReconnectCallback;
|
|
@@ -57,6 +60,20 @@ describe('when connection errors', given(a_server_sent_event_hub_connection, con
|
|
|
57
60
|
context.simulateMessage({ type: HubMessageType.Connected, payload: 'conn-2' });
|
|
58
61
|
});
|
|
59
62
|
|
|
60
|
-
it('should re-subscribe all active queries', () =>
|
|
63
|
+
it('should re-subscribe all active queries', () =>
|
|
64
|
+
context.fetchStub.calledOnce.should.equal(true));
|
|
65
|
+
|
|
66
|
+
it('should reset to legacy mode until the new connection advertises capabilities', () => {
|
|
67
|
+
const rawBody = context.fetchStub.firstCall.args[1].body as string;
|
|
68
|
+
let body: { revision?: number };
|
|
69
|
+
try {
|
|
70
|
+
body = JSON.parse(rawBody) as { revision?: number };
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
73
|
+
cause: error,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
(body.revision === undefined).should.equal(true);
|
|
77
|
+
});
|
|
61
78
|
});
|
|
62
79
|
}));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Copyright (c) Cratis. All rights reserved.
|
|
2
|
+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
|
+
|
|
4
|
+
import sinon from 'sinon';
|
|
5
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
6
|
+
import { given } from '../../../given';
|
|
7
|
+
import type { ReconnectCallback } from '../../IReconnectPolicy';
|
|
8
|
+
|
|
9
|
+
describe(
|
|
10
|
+
'when an immediate SSE open wins a pending backoff race',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
context.setup();
|
|
14
|
+
sinon.stub(console, 'warn');
|
|
15
|
+
context.connection.subscribe(
|
|
16
|
+
'query-a',
|
|
17
|
+
{ queryName: 'QueryA' },
|
|
18
|
+
sinon.stub(),
|
|
19
|
+
);
|
|
20
|
+
context.simulateError();
|
|
21
|
+
|
|
22
|
+
const delayedReconnect = context.policy.schedule.firstCall
|
|
23
|
+
.args[0] as ReconnectCallback;
|
|
24
|
+
context.connection.subscribe(
|
|
25
|
+
'query-b',
|
|
26
|
+
{ queryName: 'QueryB' },
|
|
27
|
+
sinon.stub(),
|
|
28
|
+
);
|
|
29
|
+
delayedReconnect();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => sinon.restore());
|
|
33
|
+
|
|
34
|
+
it('should create only the retired and immediate event sources', () =>
|
|
35
|
+
context.eventSources.length.should.equal(2));
|
|
36
|
+
it('should cancel the pending backoff when the immediate open wins', () =>
|
|
37
|
+
context.policy.cancel.called.should.equal(true));
|
|
38
|
+
it('should leave the immediate source connecting', () =>
|
|
39
|
+
context.eventSources[1].readyState.should.equal(EventSource.CONNECTING));
|
|
40
|
+
}),
|
|
41
|
+
);
|