@@ -15,7 +15,6 @@ describe("HealthIndicator", () => {
1515 libp2p = mockLibp2p ( ) ;
1616 events = mockEvents ( ) ;
1717 healthIndicator = new HealthIndicator ( { libp2p, events } ) ;
18- healthIndicator . start ( ) ;
1918 } ) ;
2019
2120 afterEach ( ( ) => {
@@ -28,6 +27,8 @@ describe("HealthIndicator", () => {
2827 } ) ;
2928
3029 it ( "should transition to Unhealthy when no connections" , async ( ) => {
30+ healthIndicator . start ( ) ;
31+
3132 // Only track transition, starting as healthy
3233 ( healthIndicator as any ) . value = HealthStatus . SufficientlyHealthy ;
3334
@@ -49,6 +50,8 @@ describe("HealthIndicator", () => {
4950 } ) ;
5051
5152 it ( "should transition to MinimallyHealthy with one compatible peer" , async ( ) => {
53+ healthIndicator . start ( ) ;
54+
5255 const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
5356 events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
5457 resolve ( e . detail )
@@ -70,6 +73,8 @@ describe("HealthIndicator", () => {
7073 } ) ;
7174
7275 it ( "should transition to SufficientlyHealthy with multiple compatible peers" , async ( ) => {
76+ healthIndicator . start ( ) ;
77+
7378 const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
7479 events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
7580 resolve ( e . detail )
@@ -110,11 +115,124 @@ describe("HealthIndicator", () => {
110115 expect ( removeEventSpy . firstCall . args [ 0 ] ) . to . equal ( "peer:identify" ) ;
111116 expect ( removeEventSpy . secondCall . args [ 0 ] ) . to . equal ( "peer:disconnect" ) ;
112117 } ) ;
118+
119+ it ( "should reassess health immediately when peer disconnects" , async ( ) => {
120+ const peer1 = mockPeer ( "1" , [ FilterCodecs . SUBSCRIBE , LightPushCodec ] ) ;
121+ const peer2 = mockPeer ( "2" , [ FilterCodecs . SUBSCRIBE , LightPushCodec ] ) ;
122+ const connection1 = mockConnection ( "1" ) ;
123+ const connection2 = mockConnection ( "2" ) ;
124+ const connections = [ connection1 , connection2 ] ;
125+
126+ const getConnectionsStub = sinon
127+ . stub ( libp2p , "getConnections" )
128+ . returns ( connections ) ;
129+ const peerStoreStub = sinon . stub ( libp2p . peerStore , "get" ) ;
130+ peerStoreStub . withArgs ( connection1 . remotePeer ) . resolves ( peer1 ) ;
131+ peerStoreStub . withArgs ( connection2 . remotePeer ) . resolves ( peer2 ) ;
132+
133+ const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
134+ events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
135+ resolve ( e . detail )
136+ ) ;
137+ } ) ;
138+
139+ healthIndicator . start ( ) ;
140+
141+ await statusChangePromise ;
142+ expect ( healthIndicator . toValue ( ) ) . to . equal (
143+ HealthStatus . SufficientlyHealthy
144+ ) ;
145+
146+ const statusChangePromise2 = new Promise < HealthStatus > ( ( resolve ) => {
147+ events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
148+ resolve ( e . detail )
149+ ) ;
150+ } ) ;
151+
152+ const remainingConnections = [ connection1 ] ;
153+ getConnectionsStub . returns ( remainingConnections ) ;
154+
155+ libp2p . dispatchEvent ( new CustomEvent ( "peer:disconnect" , { detail : "2" } ) ) ;
156+
157+ const changedStatus = await statusChangePromise2 ;
158+ expect ( changedStatus ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
159+ expect ( healthIndicator . toValue ( ) ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
160+ } ) ;
161+
162+ it ( "should perform initial health assessment on start" , async ( ) => {
163+ const peer = mockPeer ( "1" , [ FilterCodecs . SUBSCRIBE , LightPushCodec ] ) ;
164+ const connections = [ mockConnection ( "1" ) ] ;
165+ sinon . stub ( libp2p , "getConnections" ) . returns ( connections ) ;
166+ sinon . stub ( libp2p . peerStore , "get" ) . resolves ( peer ) ;
167+
168+ const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
169+ events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
170+ resolve ( e . detail )
171+ ) ;
172+ } ) ;
173+
174+ healthIndicator . start ( ) ;
175+
176+ const changedStatus = await statusChangePromise ;
177+ expect ( changedStatus ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
178+ expect ( healthIndicator . toValue ( ) ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
179+ } ) ;
180+
181+ it ( "should handle peer store errors gracefully" , async function ( ) {
182+ this . timeout ( 5000 ) ;
183+
184+ // Start with a healthy state
185+ ( healthIndicator as any ) . value = HealthStatus . SufficientlyHealthy ;
186+
187+ const connections = [ mockConnection ( "1" ) ] ;
188+ sinon . stub ( libp2p , "getConnections" ) . returns ( connections ) ;
189+ sinon . stub ( libp2p . peerStore , "get" ) . rejects ( new Error ( "Peer not found" ) ) ;
190+
191+ const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
192+ events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
193+ resolve ( e . detail )
194+ ) ;
195+ } ) ;
196+
197+ healthIndicator . start ( ) ;
198+
199+ const changedStatus = await statusChangePromise ;
200+ expect ( changedStatus ) . to . equal ( HealthStatus . Unhealthy ) ;
201+ expect ( healthIndicator . toValue ( ) ) . to . equal ( HealthStatus . Unhealthy ) ;
202+ } ) ;
203+
204+ it ( "should handle mixed protocol support correctly" , async ( ) => {
205+ // Start with a healthy state to ensure status change
206+ ( healthIndicator as any ) . value = HealthStatus . SufficientlyHealthy ;
207+
208+ const peer1 = mockPeer ( "1" , [ FilterCodecs . SUBSCRIBE ] ) ;
209+ const peer2 = mockPeer ( "2" , [ LightPushCodec ] ) ;
210+ const connection1 = mockConnection ( "1" ) ;
211+ const connection2 = mockConnection ( "2" ) ;
212+ const connections = [ connection1 , connection2 ] ;
213+
214+ sinon . stub ( libp2p , "getConnections" ) . returns ( connections ) ;
215+ const peerStoreStub = sinon . stub ( libp2p . peerStore , "get" ) ;
216+ peerStoreStub . withArgs ( connection1 . remotePeer ) . resolves ( peer1 ) ;
217+ peerStoreStub . withArgs ( connection2 . remotePeer ) . resolves ( peer2 ) ;
218+
219+ const statusChangePromise = new Promise < HealthStatus > ( ( resolve ) => {
220+ events . addEventListener ( "waku:health" , ( e : CustomEvent < HealthStatus > ) =>
221+ resolve ( e . detail )
222+ ) ;
223+ } ) ;
224+
225+ healthIndicator . start ( ) ;
226+
227+ const changedStatus = await statusChangePromise ;
228+ expect ( changedStatus ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
229+ expect ( healthIndicator . toValue ( ) ) . to . equal ( HealthStatus . MinimallyHealthy ) ;
230+ } ) ;
113231} ) ;
114232
115233function mockLibp2p ( ) : Libp2p {
116234 const peerStore = {
117- get : ( id : any ) => Promise . resolve ( mockPeer ( id . toString ( ) , [ ] ) )
235+ get : ( ) => Promise . reject ( new Error ( "Peer not found" ) )
118236 } ;
119237
120238 const events = new EventTarget ( ) ;
0 commit comments