@@ -6,17 +6,56 @@ import {
66 getEnvConfig ,
77 RedisConnectionConfig ,
88} from "./test-scenario.util" ;
9- import { createClient } from "../../.." ;
9+ import { createClient , RedisClientOptions } from "../../.." ;
1010import { before } from "mocha" ;
11- import { spy } from "sinon" ;
11+ import Sinon , { SinonSpy , spy , stub } from "sinon" ;
1212import assert from "node:assert" ;
13- import net from "node:net" ;
13+
14+ /**
15+ * Creates a spy on a duplicated client method
16+ * @param client - The Redis client instance
17+ * @param funcName - The name of the method to spy on
18+ * @returns Object containing the promise that resolves with the spy and restore function
19+ */
20+ const spyOnTemporaryClientInstanceMethod = (
21+ client : ReturnType < typeof createClient < any , any , any , any > > ,
22+ methodName : string
23+ ) => {
24+ const { promise, resolve } = (
25+ Promise as typeof Promise & {
26+ withResolvers : ( ) => {
27+ promise : Promise < { spy : SinonSpy < any [ ] , any > ; restore : ( ) => void } > ;
28+ resolve : ( value : any ) => void ;
29+ } ;
30+ }
31+ ) . withResolvers ( ) ;
32+
33+ const originalDuplicate = client . duplicate . bind ( client ) ;
34+
35+ const duplicateStub : Sinon . SinonStub < any [ ] , any > = stub (
36+ // Temporary clients (in the context of hitless upgrade)
37+ // are created by calling the duplicate method on the client.
38+ Object . getPrototypeOf ( client ) ,
39+ "duplicate"
40+ ) . callsFake ( ( opts ) => {
41+ const tmpClient = originalDuplicate ( opts ) ;
42+ resolve ( {
43+ spy : spy ( tmpClient , methodName ) ,
44+ restore : duplicateStub . restore ,
45+ } ) ;
46+
47+ return tmpClient ;
48+ } ) ;
49+
50+ return {
51+ getSpy : ( ) => promise ,
52+ } ;
53+ } ;
1454
1555describe ( "Connection Handoff" , ( ) => {
1656 let clientConfig : RedisConnectionConfig ;
1757 let client : ReturnType < typeof createClient < any , any , any , any > > ;
1858 let faultInjectorClient : FaultInjectorClient ;
19- let connectSpy = spy ( net , "createConnection" ) ;
2059
2160 before ( ( ) => {
2261 const envConfig = getEnvConfig ( ) ;
@@ -28,62 +67,110 @@ describe("Connection Handoff", () => {
2867 clientConfig = getDatabaseConfig ( redisConfig ) ;
2968 } ) ;
3069
31- beforeEach ( async ( ) => {
32- connectSpy . resetHistory ( ) ;
33-
34- client = await createTestClient ( clientConfig ) ;
35-
36- await client . flushAll ( ) ;
37- } ) ;
38-
39- afterEach ( ( ) => {
70+ afterEach ( async ( ) => {
4071 if ( client && client . isOpen ) {
72+ await client . flushAll ( ) ;
4173 client . destroy ( ) ;
4274 }
4375 } ) ;
4476
45- describe ( "New Connection Establishment" , ( ) => {
46- it ( "should establish new connection" , async ( ) => {
47- assert . equal ( connectSpy . callCount , 1 ) ;
48-
49- const { action_id : lowTimeoutBindAndMigrateActionId } =
50- await faultInjectorClient . migrateAndBindAction ( {
51- bdbId : clientConfig . bdbId ,
52- clusterIndex : 0 ,
53- } ) ;
54-
55- await faultInjectorClient . waitForAction ( lowTimeoutBindAndMigrateActionId ) ;
56-
57- assert . equal ( connectSpy . callCount , 2 ) ;
58- } ) ;
77+ describe ( "New Connection Establishment & Traffic Resumption" , ( ) => {
78+ const cases : Array < {
79+ name : string ;
80+ clientOptions : Partial < RedisClientOptions > ;
81+ } > = [
82+ {
83+ name : "default options" ,
84+ clientOptions : { } ,
85+ } ,
86+ {
87+ name : "external-ip" ,
88+ clientOptions : {
89+ maintMovingEndpointType : "external-ip" ,
90+ } ,
91+ } ,
92+ {
93+ name : "external-fqdn" ,
94+ clientOptions : {
95+ maintMovingEndpointType : "external-fqdn" ,
96+ } ,
97+ } ,
98+ {
99+ name : "auto" ,
100+ clientOptions : {
101+ maintMovingEndpointType : "auto" ,
102+ } ,
103+ } ,
104+ {
105+ name : "none" ,
106+ clientOptions : {
107+ maintMovingEndpointType : "none" ,
108+ } ,
109+ } ,
110+ ] ;
111+
112+ for ( const { name, clientOptions } of cases ) {
113+ it . only ( `should establish new connection and resume traffic afterwards - ${ name } ` , async ( ) => {
114+ client = await createTestClient ( clientConfig , clientOptions ) ;
115+
116+ const spyObject = spyOnTemporaryClientInstanceMethod ( client , "connect" ) ;
117+
118+ // PART 1 Establish initial connection
119+ const { action_id : lowTimeoutBindAndMigrateActionId } =
120+ await faultInjectorClient . migrateAndBindAction ( {
121+ bdbId : clientConfig . bdbId ,
122+ clusterIndex : 0 ,
123+ } ) ;
124+
125+ await faultInjectorClient . waitForAction (
126+ lowTimeoutBindAndMigrateActionId
127+ ) ;
128+
129+ const spyResult = await spyObject . getSpy ( ) ;
130+
131+ assert . strictEqual ( spyResult . spy . callCount , 1 ) ;
132+
133+ // PART 2 Verify traffic resumption
134+ const currentTime = Date . now ( ) . toString ( ) ;
135+ await client . set ( "key" , currentTime ) ;
136+ const result = await client . get ( "key" ) ;
137+
138+ assert . strictEqual ( result , currentTime ) ;
139+
140+ spyResult . restore ( ) ;
141+ } ) ;
142+ }
59143 } ) ;
60144
61145 describe ( "TLS Connection Handoff" , ( ) => {
62- it ( "TODO receiveMessagesWithTLSEnabledTest" , async ( ) => {
146+ it . skip ( "TODO receiveMessagesWithTLSEnabledTest" , async ( ) => {
63147 //
64148 } ) ;
65- it ( "TODO connectionHandoffWithStaticInternalNameTest" , async ( ) => {
149+ it . skip ( "TODO connectionHandoffWithStaticInternalNameTest" , async ( ) => {
66150 //
67151 } ) ;
68- it ( "TODO connectionHandoffWithStaticExternalNameTest" , async ( ) => {
152+ it . skip ( "TODO connectionHandoffWithStaticExternalNameTest" , async ( ) => {
69153 //
70154 } ) ;
71155 } ) ;
72156
73- describe ( "Traffic Resumption" , ( ) => {
74- it ( "Traffic resumed after handoff" , async ( ) => {
75- const { action_id } = await faultInjectorClient . migrateAndBindAction ( {
76- bdbId : clientConfig . bdbId ,
77- clusterIndex : 0 ,
78- } ) ;
157+ describe ( "Connection Cleanup" , ( ) => {
158+ it ( "should shut down old connection" , async ( ) => {
159+ const spyObject = spyOnTemporaryClientInstanceMethod ( client , "destroy" ) ;
160+
161+ const { action_id : lowTimeoutBindAndMigrateActionId } =
162+ await faultInjectorClient . migrateAndBindAction ( {
163+ bdbId : clientConfig . bdbId ,
164+ clusterIndex : 0 ,
165+ } ) ;
166+
167+ await faultInjectorClient . waitForAction ( lowTimeoutBindAndMigrateActionId ) ;
79168
80- await faultInjectorClient . waitForAction ( action_id ) ;
169+ const spyResult = await spyObject . getSpy ( ) ;
81170
82- const currentTime = Date . now ( ) . toString ( ) ;
83- await client . set ( "key" , currentTime ) ;
84- const result = await client . get ( "key" ) ;
171+ assert . equal ( spyResult . spy . callCount , 1 ) ;
85172
86- assert . strictEqual ( result , currentTime ) ;
173+ spyResult . restore ( ) ;
87174 } ) ;
88175 } ) ;
89176} ) ;
0 commit comments