@cratis/arc 22.0.2 → 22.1.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,92 @@
|
|
|
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 { HubMessageType } from '../../WebSocketHubConnection';
|
|
8
|
+
|
|
9
|
+
describe(
|
|
10
|
+
'when messages arrive from a retired subscription generation',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
const queryId = 'q1';
|
|
13
|
+
let originalCallback: sinon.SinonStub;
|
|
14
|
+
let replacementCallback: sinon.SinonStub;
|
|
15
|
+
let originalGeneration: number;
|
|
16
|
+
let replacementGeneration: number;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
context.setup();
|
|
20
|
+
sinon.stub(console, 'warn');
|
|
21
|
+
sinon.stub(console, 'error');
|
|
22
|
+
originalCallback = sinon.stub();
|
|
23
|
+
replacementCallback = sinon.stub();
|
|
24
|
+
|
|
25
|
+
context.connection.subscribe(
|
|
26
|
+
queryId,
|
|
27
|
+
{ queryName: 'OriginalQuery' },
|
|
28
|
+
originalCallback,
|
|
29
|
+
);
|
|
30
|
+
context.simulateOpen();
|
|
31
|
+
context.simulateMessage({
|
|
32
|
+
type: HubMessageType.Connected,
|
|
33
|
+
payload: 'connection-a',
|
|
34
|
+
supportsSubscriptionRevisions: true,
|
|
35
|
+
});
|
|
36
|
+
originalGeneration = getGeneration(0);
|
|
37
|
+
|
|
38
|
+
context.connection.subscribe(
|
|
39
|
+
queryId,
|
|
40
|
+
{ queryName: 'ReplacementQuery' },
|
|
41
|
+
replacementCallback,
|
|
42
|
+
);
|
|
43
|
+
replacementGeneration = getGeneration(1);
|
|
44
|
+
|
|
45
|
+
context.simulateMessage({
|
|
46
|
+
type: HubMessageType.QueryResult,
|
|
47
|
+
queryId,
|
|
48
|
+
revision: originalGeneration,
|
|
49
|
+
payload: { isSuccess: true, data: ['stale'] },
|
|
50
|
+
});
|
|
51
|
+
context.simulateMessage({
|
|
52
|
+
type: HubMessageType.Unauthorized,
|
|
53
|
+
queryId,
|
|
54
|
+
revision: originalGeneration,
|
|
55
|
+
});
|
|
56
|
+
context.simulateMessage({
|
|
57
|
+
type: HubMessageType.Error,
|
|
58
|
+
queryId,
|
|
59
|
+
revision: originalGeneration,
|
|
60
|
+
payload: 'stale error',
|
|
61
|
+
});
|
|
62
|
+
context.simulateMessage({
|
|
63
|
+
type: HubMessageType.QueryResult,
|
|
64
|
+
queryId,
|
|
65
|
+
revision: replacementGeneration,
|
|
66
|
+
payload: { isSuccess: true, data: ['current'] },
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
afterEach(() => sinon.restore());
|
|
71
|
+
|
|
72
|
+
it('should not notify the retired callback', () =>
|
|
73
|
+
originalCallback.called.should.be.false);
|
|
74
|
+
it('should deliver the current generation result', () =>
|
|
75
|
+
replacementCallback.calledOnce.should.be.true);
|
|
76
|
+
it('should retain the replacement after stale unauthorized', () =>
|
|
77
|
+
context.connection.queryCount.should.equal(1));
|
|
78
|
+
it('should not log the stale server error', () =>
|
|
79
|
+
(console.error as sinon.SinonStub).called.should.be.false);
|
|
80
|
+
|
|
81
|
+
function getGeneration(callIndex: number): number {
|
|
82
|
+
const rawBody = context.fetchStub.getCall(callIndex).args[1].body as string;
|
|
83
|
+
try {
|
|
84
|
+
return (JSON.parse(rawBody) as { revision: number }).revision;
|
|
85
|
+
} catch (error) {
|
|
86
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
87
|
+
cause: error,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
@@ -6,37 +6,76 @@ 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
|
-
context.connection.subscribe(queryId, { queryName: 'MyQuery' }, sinon.stub());
|
|
16
|
-
context.simulateOpen();
|
|
17
|
-
context.simulateMessage({ type: HubMessageType.Connected, payload: connectionId });
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
afterEach(() => sinon.restore());
|
|
21
|
-
|
|
22
|
-
it('should POST to the subscribe URL', () => context.fetchStub.calledOnce.should.be.true);
|
|
23
|
-
it('should pass the connection id in the request body', () => {
|
|
24
|
-
const body = JSON.parse(context.fetchStub.getCall(0).args[1].body);
|
|
25
|
-
body.connectionId.should.equal(connectionId);
|
|
26
|
-
});
|
|
27
|
-
it('should pass the query id in the request body', () => {
|
|
28
|
-
const body = JSON.parse(context.fetchStub.getCall(0).args[1].body);
|
|
29
|
-
body.queryId.should.equal(queryId);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('when a query result message arrives', () => {
|
|
33
|
-
const result = { isSuccess: true, data: ['x'] };
|
|
9
|
+
describe(
|
|
10
|
+
'when subscribing sends the subscribe POST after Connected',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
const queryId = 'q1';
|
|
13
|
+
const connectionId = 'conn-abc';
|
|
14
|
+
let callback: sinon.SinonStub;
|
|
34
15
|
|
|
35
16
|
beforeEach(() => {
|
|
36
|
-
context.
|
|
17
|
+
context.setup();
|
|
18
|
+
callback = sinon.stub();
|
|
19
|
+
context.connection.subscribe(queryId, { queryName: 'MyQuery' }, callback);
|
|
20
|
+
context.simulateOpen();
|
|
21
|
+
context.simulateMessage({
|
|
22
|
+
type: HubMessageType.Connected,
|
|
23
|
+
payload: connectionId,
|
|
24
|
+
supportsSubscriptionRevisions: true,
|
|
25
|
+
});
|
|
37
26
|
});
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
28
|
+
afterEach(() => sinon.restore());
|
|
29
|
+
|
|
30
|
+
it('should POST to the subscribe URL', () =>
|
|
31
|
+
context.fetchStub.calledOnce.should.be.true);
|
|
32
|
+
it('should pass the connection id in the request body', () =>
|
|
33
|
+
getBody().connectionId.should.equal(connectionId));
|
|
34
|
+
it('should pass the query id in the request body', () =>
|
|
35
|
+
getBody().queryId.should.equal(queryId));
|
|
36
|
+
it('should pass a positive numeric revision in the request body', () =>
|
|
37
|
+
getBody().revision.should.equal(1));
|
|
38
|
+
|
|
39
|
+
function getBody(): {
|
|
40
|
+
connectionId: string;
|
|
41
|
+
queryId: string;
|
|
42
|
+
revision: number;
|
|
43
|
+
} {
|
|
44
|
+
const rawBody = context.fetchStub.getCall(0).args[1].body as string;
|
|
45
|
+
try {
|
|
46
|
+
return JSON.parse(rawBody) as {
|
|
47
|
+
connectionId: string;
|
|
48
|
+
queryId: string;
|
|
49
|
+
revision: number;
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
throw new Error('Expected a valid subscribe request body', {
|
|
53
|
+
cause: error,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe('when a query result message arrives', () => {
|
|
59
|
+
const result = { isSuccess: true, data: ['x'] };
|
|
60
|
+
|
|
61
|
+
beforeEach(() => {
|
|
62
|
+
context.simulateMessage({
|
|
63
|
+
type: HubMessageType.QueryResult,
|
|
64
|
+
queryId,
|
|
65
|
+
payload: { isSuccess: true, data: ['legacy'] },
|
|
66
|
+
});
|
|
67
|
+
context.simulateMessage({
|
|
68
|
+
type: HubMessageType.QueryResult,
|
|
69
|
+
queryId,
|
|
70
|
+
revision: getBody().revision,
|
|
71
|
+
payload: result,
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should ignore the missing-revision frame and deliver the exact revision once', () => {
|
|
76
|
+
callback.calledOnce.should.equal(true);
|
|
77
|
+
callback.firstCall.args[0].should.deep.equal(result);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
@@ -0,0 +1,69 @@
|
|
|
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 { given } from '../../../given';
|
|
6
|
+
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
7
|
+
import { a_server_sent_event_hub_connection } from '../given/a_server_sent_event_hub_connection';
|
|
8
|
+
|
|
9
|
+
describe(
|
|
10
|
+
'when the SSE server does not advertise subscription revision support',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
const queryId = 'query-1';
|
|
13
|
+
let callback: sinon.SinonStub;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
context.setup();
|
|
17
|
+
callback = sinon.stub();
|
|
18
|
+
context.connection.subscribe(queryId, { queryName: 'MyQuery' }, callback);
|
|
19
|
+
context.simulateOpen();
|
|
20
|
+
context.simulateMessage({
|
|
21
|
+
type: HubMessageType.Connected,
|
|
22
|
+
payload: 'legacy-connection',
|
|
23
|
+
});
|
|
24
|
+
context.simulateMessage({
|
|
25
|
+
type: HubMessageType.QueryResult,
|
|
26
|
+
queryId,
|
|
27
|
+
payload: { data: ['legacy'] },
|
|
28
|
+
});
|
|
29
|
+
context.connection.unsubscribe(queryId);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => sinon.restore());
|
|
33
|
+
|
|
34
|
+
it('should omit the revision from the subscribe request', () =>
|
|
35
|
+
(subscribeBody().revision === undefined).should.equal(true));
|
|
36
|
+
|
|
37
|
+
it('should accept a missing-revision result frame', () => {
|
|
38
|
+
callback.calledOnce.should.equal(true);
|
|
39
|
+
callback.firstCall.args[0].should.deep.equal({ data: ['legacy'] });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should omit the revision from the unsubscribe request', () =>
|
|
43
|
+
(unsubscribeBody().revision === undefined).should.equal(true));
|
|
44
|
+
|
|
45
|
+
function subscribeBody(): { revision?: number } {
|
|
46
|
+
return parseBody(context.fetchStub.firstCall.args[1].body as string);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function unsubscribeBody(): { revision?: number } {
|
|
50
|
+
const unsubscribeCall = context.fetchStub
|
|
51
|
+
.getCalls()
|
|
52
|
+
.find((call) => (call.args[0] as string).includes('unsubscribe'));
|
|
53
|
+
if (!unsubscribeCall) {
|
|
54
|
+
throw new Error('Expected an unsubscribe request');
|
|
55
|
+
}
|
|
56
|
+
return parseBody(unsubscribeCall.args[1].body as string);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseBody(rawBody: string): { revision?: number } {
|
|
60
|
+
try {
|
|
61
|
+
return JSON.parse(rawBody) as { revision?: number };
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Error('Expected a valid subscription request body', {
|
|
64
|
+
cause: error,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
@@ -6,27 +6,46 @@ 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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
describe(
|
|
10
|
+
'when unsubscribing the only query',
|
|
11
|
+
given(a_server_sent_event_hub_connection, (context) => {
|
|
12
|
+
beforeEach(async () => {
|
|
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-abc',
|
|
19
|
+
supportsSubscriptionRevisions: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
context.connection.unsubscribe('q1');
|
|
23
|
+
|
|
24
|
+
// Allow the micro-task queue to drain so the async fetch completes.
|
|
25
|
+
await Promise.resolve();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => sinon.restore());
|
|
29
|
+
|
|
30
|
+
it('should close the event source', () =>
|
|
31
|
+
context.fakeEventSource.close.calledOnce.should.be.true);
|
|
32
|
+
|
|
33
|
+
it('should send an unsubscribe POST request with the exact revision', () => {
|
|
34
|
+
const unsubscribeCalls = context.fetchStub.args.filter(
|
|
35
|
+
(args: unknown[]) =>
|
|
36
|
+
typeof args[0] === 'string' &&
|
|
37
|
+
(args[0] as string).includes('unsubscribe'),
|
|
38
|
+
);
|
|
39
|
+
unsubscribeCalls.length.should.equal(1);
|
|
40
|
+
getBody(unsubscribeCalls[0][1]).revision.should.equal(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function getBody(init: RequestInit): { revision: number } {
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(init.body as string) as { revision: number };
|
|
46
|
+
} catch (error) {
|
|
47
|
+
throw new Error('Expected a valid unsubscribe body', { cause: error });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
@@ -7,41 +7,65 @@ import { given } from '../../../given';
|
|
|
7
7
|
import { HubMessageType } from '../../WebSocketHubConnection';
|
|
8
8
|
import { QueryResult } from '../../QueryResult';
|
|
9
9
|
|
|
10
|
-
describe(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
describe(
|
|
11
|
+
'when receiving unauthorized for a subscribed query',
|
|
12
|
+
given(a_web_socket_hub_connection, (context) => {
|
|
13
|
+
let callback: sinon.SinonStub;
|
|
14
|
+
let receivedResult: QueryResult<unknown> | undefined;
|
|
15
|
+
const queryId = 'q-auth-1';
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
context.setup();
|
|
19
|
+
callback = sinon.stub();
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
context.connection.subscribe(queryId, { queryName: 'SecureQuery' }, callback);
|
|
22
|
+
context.simulateOpen();
|
|
23
|
+
context.simulateMessage({
|
|
24
|
+
type: HubMessageType.Connected,
|
|
25
|
+
supportsSubscriptionRevisions: true,
|
|
26
|
+
});
|
|
27
|
+
const subscribe = getSubscribeMessage();
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
// Server replies with Unauthorized for this exact subscription.
|
|
30
|
+
context.simulateMessage({
|
|
31
|
+
type: HubMessageType.Unauthorized,
|
|
32
|
+
queryId,
|
|
33
|
+
revision: subscribe.revision,
|
|
34
|
+
});
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
receivedResult = callback.firstCall?.args[0] as
|
|
37
|
+
| QueryResult<unknown>
|
|
38
|
+
| undefined;
|
|
39
|
+
});
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
sinon.restore();
|
|
43
|
+
});
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
function getSubscribeMessage(): { revision: number } {
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse(context.fakeSocket.send.getCall(1).args[0]) as {
|
|
48
|
+
revision: number;
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error('Expected a valid subscribe message', { cause: error });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
35
54
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
it('should invoke the subscriber callback', () => {
|
|
56
|
+
callback.calledOnce.should.equal(true);
|
|
57
|
+
});
|
|
39
58
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
59
|
+
it('should report isAuthorized as false', () => {
|
|
60
|
+
receivedResult!.isAuthorized.should.equal(false);
|
|
61
|
+
});
|
|
43
62
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
63
|
+
it('should report isSuccess as false', () => {
|
|
64
|
+
receivedResult!.isSuccess.should.equal(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should remove the subscription', () => {
|
|
68
|
+
context.connection.queryCount.should.equal(0);
|
|
69
|
+
});
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
@@ -0,0 +1,131 @@
|
|
|
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_web_socket_hub_connection } from '../given/a_web_socket_hub_connection';
|
|
6
|
+
import { given } from '../../../given';
|
|
7
|
+
import { type HubMessage, HubMessageType } from '../../WebSocketHubConnection';
|
|
8
|
+
import type { ReconnectCallback } from '../../IReconnectPolicy';
|
|
9
|
+
|
|
10
|
+
for (const supportsSubscriptionRevisions of [false, true]) {
|
|
11
|
+
const mode = supportsSubscriptionRevisions ? 'revision-aware' : 'legacy';
|
|
12
|
+
|
|
13
|
+
describe(`when an immediate ${mode} socket open wins during reconnect backoff`, given(a_web_socket_hub_connection, context => {
|
|
14
|
+
const queryId = 'query-a';
|
|
15
|
+
let callback: sinon.SinonStub;
|
|
16
|
+
let secondSocket: typeof context.fakeSocket;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
context.setup();
|
|
20
|
+
sinon.stub(console, 'log');
|
|
21
|
+
sinon.stub(console, 'warn');
|
|
22
|
+
sinon.stub(console, 'error');
|
|
23
|
+
callback = sinon.stub();
|
|
24
|
+
|
|
25
|
+
context.connection.subscribe(queryId, { queryName: 'Original' }, sinon.stub());
|
|
26
|
+
const firstSocket = context.fakeSocket;
|
|
27
|
+
firstSocket.readyState = WebSocket.OPEN;
|
|
28
|
+
firstSocket.onopen?.();
|
|
29
|
+
if (supportsSubscriptionRevisions) {
|
|
30
|
+
firstSocket.onmessage?.(messageEvent({
|
|
31
|
+
type: HubMessageType.Connected,
|
|
32
|
+
supportsSubscriptionRevisions: true,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const staleMessage = firstSocket.onmessage!;
|
|
37
|
+
const staleClose = firstSocket.onclose!;
|
|
38
|
+
const staleError = firstSocket.onerror!;
|
|
39
|
+
firstSocket.readyState = WebSocket.CLOSED;
|
|
40
|
+
staleClose();
|
|
41
|
+
|
|
42
|
+
secondSocket = createSocket();
|
|
43
|
+
context.WebSocketStub.onSecondCall().returns(secondSocket);
|
|
44
|
+
context.connection.subscribe(queryId, { queryName: 'Replacement' }, callback);
|
|
45
|
+
|
|
46
|
+
const reconnectCallback = (context.policy.schedule as sinon.SinonStub).firstCall.args[0] as ReconnectCallback;
|
|
47
|
+
reconnectCallback();
|
|
48
|
+
|
|
49
|
+
secondSocket.readyState = WebSocket.OPEN;
|
|
50
|
+
secondSocket.onopen?.();
|
|
51
|
+
if (supportsSubscriptionRevisions) {
|
|
52
|
+
secondSocket.onmessage?.(messageEvent({
|
|
53
|
+
type: HubMessageType.Connected,
|
|
54
|
+
supportsSubscriptionRevisions: true,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const revision = supportsSubscriptionRevisions
|
|
59
|
+
? sentMessages(secondSocket).find(message => message.type === HubMessageType.Subscribe && message.revision !== undefined)!.revision
|
|
60
|
+
: undefined;
|
|
61
|
+
|
|
62
|
+
staleMessage(messageEvent({
|
|
63
|
+
type: HubMessageType.Connected,
|
|
64
|
+
supportsSubscriptionRevisions: true,
|
|
65
|
+
}));
|
|
66
|
+
staleMessage(messageEvent({
|
|
67
|
+
type: HubMessageType.QueryResult,
|
|
68
|
+
queryId,
|
|
69
|
+
revision,
|
|
70
|
+
payload: { data: ['stale'] },
|
|
71
|
+
}));
|
|
72
|
+
staleMessage(messageEvent({
|
|
73
|
+
type: HubMessageType.Unauthorized,
|
|
74
|
+
queryId,
|
|
75
|
+
revision,
|
|
76
|
+
}));
|
|
77
|
+
staleMessage(messageEvent({
|
|
78
|
+
type: HubMessageType.Error,
|
|
79
|
+
queryId,
|
|
80
|
+
revision,
|
|
81
|
+
payload: 'stale',
|
|
82
|
+
}));
|
|
83
|
+
staleError({} as Event);
|
|
84
|
+
staleClose();
|
|
85
|
+
|
|
86
|
+
secondSocket.onmessage?.(messageEvent({
|
|
87
|
+
type: HubMessageType.QueryResult,
|
|
88
|
+
queryId,
|
|
89
|
+
revision,
|
|
90
|
+
payload: { data: ['current'] },
|
|
91
|
+
}));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
afterEach(() => {
|
|
95
|
+
context.connection.dispose();
|
|
96
|
+
sinon.restore();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should cancel the pending reconnect when the immediate open wins', () =>
|
|
100
|
+
(context.policy.cancel as sinon.SinonStub).calledOnce.should.be.true);
|
|
101
|
+
it('should not let the pending callback create a third socket', () =>
|
|
102
|
+
context.WebSocketStub.callCount.should.equal(2));
|
|
103
|
+
it('should not close the current socket', () => secondSocket.close.called.should.be.false);
|
|
104
|
+
it('should not schedule another reconnect from the stale close', () =>
|
|
105
|
+
(context.policy.schedule as sinon.SinonStub).calledOnce.should.be.true);
|
|
106
|
+
it('should not process stale result or unauthorized frames', () => callback.calledOnce.should.be.true);
|
|
107
|
+
it('should retain the current subscription', () => context.connection.queryCount.should.equal(1));
|
|
108
|
+
it('should not process the stale error frame or callback', () =>
|
|
109
|
+
(console.error as sinon.SinonStub).called.should.be.false);
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createSocket() {
|
|
114
|
+
return {
|
|
115
|
+
onopen: null as (() => void) | null,
|
|
116
|
+
onclose: null as (() => void) | null,
|
|
117
|
+
onerror: null as ((error: Event) => void) | null,
|
|
118
|
+
onmessage: null as ((event: MessageEvent) => void) | null,
|
|
119
|
+
send: sinon.stub(),
|
|
120
|
+
close: sinon.stub(),
|
|
121
|
+
readyState: WebSocket.CONNECTING as number,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function messageEvent(message: HubMessage): MessageEvent {
|
|
126
|
+
return { data: JSON.stringify(message) } as MessageEvent;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function sentMessages(socket: ReturnType<typeof createSocket>): HubMessage[] {
|
|
130
|
+
return socket.send.args.map(args => JSON.parse(args[0]) as HubMessage);
|
|
131
|
+
}
|
package/queries/for_WebSocketHubConnection/when_reconnecting/replaces_a_closing_socket_safely.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
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_web_socket_hub_connection } from '../given/a_web_socket_hub_connection';
|
|
6
|
+
import { given } from '../../../given';
|
|
7
|
+
|
|
8
|
+
describe('when opening while the owned socket is closing', given(a_web_socket_hub_connection, context => {
|
|
9
|
+
let firstSocket: typeof context.fakeSocket;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
context.setup();
|
|
13
|
+
context.connection.subscribe('q1', { queryName: 'First' }, sinon.stub());
|
|
14
|
+
firstSocket = context.fakeSocket;
|
|
15
|
+
firstSocket.readyState = WebSocket.CLOSING;
|
|
16
|
+
context.WebSocketStub.onSecondCall().returns({
|
|
17
|
+
onopen: null,
|
|
18
|
+
onclose: null,
|
|
19
|
+
onerror: null,
|
|
20
|
+
onmessage: null,
|
|
21
|
+
send: sinon.stub(),
|
|
22
|
+
close: sinon.stub(),
|
|
23
|
+
readyState: WebSocket.CONNECTING,
|
|
24
|
+
});
|
|
25
|
+
context.connection.subscribe('q2', { queryName: 'Second' }, sinon.stub());
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
context.connection.dispose();
|
|
30
|
+
sinon.restore();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should close the replaced socket', () => firstSocket.close.calledOnce.should.be.true);
|
|
34
|
+
it('should detach the replaced socket open handler', () => (firstSocket.onopen === null).should.be.true);
|
|
35
|
+
it('should detach the replaced socket close handler', () => (firstSocket.onclose === null).should.be.true);
|
|
36
|
+
it('should detach the replaced socket error handler', () => (firstSocket.onerror === null).should.be.true);
|
|
37
|
+
it('should detach the replaced socket message handler', () => (firstSocket.onmessage === null).should.be.true);
|
|
38
|
+
it('should create one replacement socket', () => context.WebSocketStub.callCount.should.equal(2));
|
|
39
|
+
}));
|