@@ -10,6 +10,8 @@ import {
1010} from '@mongodb-js/testing-library-compass' ;
1111import React from 'react' ;
1212import { InMemoryConnectionStorage } from '@mongodb-js/connection-storage/provider' ;
13+ import { getDataServiceForConnection } from './connections-store-redux' ;
14+ import { type ConnectionInfo } from '@mongodb-js/connection-info' ;
1315
1416const mockConnections = [
1517 {
@@ -34,6 +36,13 @@ const mockConnections = [
3436 } ,
3537] ;
3638
39+ const connectionInfoWithAtlasMetadata = {
40+ ...createDefaultConnectionInfo ( ) ,
41+ atlasMetadata : {
42+ clusterName : 'pineapple' ,
43+ } as ConnectionInfo [ 'atlasMetadata' ] ,
44+ } ;
45+
3746function renderCompassConnections ( opts ?: RenderConnectionsOptions ) {
3847 return render (
3948 < div >
@@ -274,6 +283,101 @@ describe('CompassConnections store', function () {
274283 await connectionStorage . load ( { id : mockConnections [ 0 ] . id } )
275284 ) . to . have . nested . property ( 'favorite.name' , 'turtles' ) ;
276285 } ) ;
286+
287+ it ( 'should ignore server heartbeat failed events that are not non-retryable error codes' , async function ( ) {
288+ const { connectionsStore } = renderCompassConnections ( {
289+ connectFn : async ( ) => {
290+ await wait ( 1 ) ;
291+ return { } ;
292+ } ,
293+ } ) ;
294+
295+ // Wait till we're connected.
296+ await connectionsStore . actions . connect ( connectionInfoWithAtlasMetadata ) ;
297+
298+ const connections = connectionsStore . getState ( ) . connections ;
299+ expect ( connections . ids ) . to . have . lengthOf ( 1 ) ;
300+
301+ const dataService = getDataServiceForConnection (
302+ connectionInfoWithAtlasMetadata . id
303+ ) ;
304+
305+ let didDisconnect = false ;
306+ let didCheckForConnected = false ;
307+ sinon . stub ( dataService , 'disconnect' ) . callsFake ( async ( ) => {
308+ didDisconnect = true ;
309+ return Promise . resolve ( ) ;
310+ } ) ;
311+ dataService . isConnected = ( ) => {
312+ // If this is called we know the error wasn't handled properly.
313+ didCheckForConnected = true ;
314+ return true ;
315+ } ;
316+
317+ let didReceiveCallToHeartbeatFailedListener = false ;
318+ dataService . on ( 'serverHeartbeatFailed' , ( ) => {
319+ didReceiveCallToHeartbeatFailedListener = true ;
320+ } ) ;
321+
322+ // Send a heartbeat fail with an error that's not a non-retryable error code.
323+ dataService [ 'emit' ] ( 'serverHeartbeatFailed' , {
324+ failure : new Error ( 'code: 1234, Not the error we are looking for' ) ,
325+ } ) ;
326+
327+ // Wait for the listener to handle the message.
328+ await waitFor ( ( ) => {
329+ expect ( didReceiveCallToHeartbeatFailedListener ) . to . be . true ;
330+ } ) ;
331+ await wait ( 1 ) ;
332+
333+ expect ( didDisconnect ) . to . be . false ;
334+ expect ( didCheckForConnected ) . to . be . false ;
335+ } ) ;
336+
337+ it ( 'should listen for non-retryable errors on server heartbeat failed events and disconnect the data service when encountered' , async function ( ) {
338+ const { connectionsStore } = renderCompassConnections ( {
339+ connectFn : async ( ) => {
340+ await wait ( 1 ) ;
341+ return { } ;
342+ } ,
343+ } ) ;
344+
345+ // Wait till we're connected.
346+ await connectionsStore . actions . connect ( connectionInfoWithAtlasMetadata ) ;
347+
348+ const connections = connectionsStore . getState ( ) . connections ;
349+ expect ( connections . ids ) . to . have . lengthOf ( 1 ) ;
350+
351+ const dataService = getDataServiceForConnection (
352+ connectionInfoWithAtlasMetadata . id
353+ ) ;
354+
355+ let didDisconnect = false ;
356+ sinon . stub ( dataService , 'disconnect' ) . callsFake ( async ( ) => {
357+ didDisconnect = true ;
358+ return Promise . resolve ( ) ;
359+ } ) ;
360+ dataService . isConnected = ( ) => true ;
361+
362+ // Send a heartbeat fail with an error that's a non-retryable error code.
363+ dataService [ 'emit' ] ( 'serverHeartbeatFailed' , {
364+ failure : new Error ( 'code: 3003, reason: Insufficient permissions' ) ,
365+ } ) ;
366+
367+ await waitFor ( ( ) => {
368+ expect ( didDisconnect ) . to . be . true ;
369+ } ) ;
370+
371+ await waitFor ( function ( ) {
372+ const titleNode = screen . getByText ( 'Unable to connect to pineapple' ) ;
373+ expect ( titleNode ) . to . be . visible ;
374+
375+ const descriptionNode = screen . getByText (
376+ 'Reason: Insufficient permissions. To use continue to use this connection either disconnect and reconnect, or refresh your page.'
377+ ) ;
378+ expect ( descriptionNode ) . to . be . visible ;
379+ } ) ;
380+ } ) ;
277381 } ) ;
278382
279383 describe ( '#saveAndConnect' , function ( ) {
0 commit comments