11import { logger } from "@coder/logger"
22import { readFile , writeFile , stat , utimes } from "fs/promises"
3- import { Heart , heartbeatTimer } from "../../../src/node/heart"
4- import { wrapper } from "../../../src/node/wrapper"
3+ import { Heart } from "../../../src/node/heart"
54import { clean , mockLogger , tmpdir } from "../../utils/helpers"
65
76const mockIsActive = ( resolveTo : boolean ) => jest . fn ( ) . mockResolvedValue ( resolveTo )
87
9- jest . mock ( "../../../src/node/wrapper" , ( ) => {
10- const original = jest . requireActual ( "../../../src/node/wrapper" )
11- return {
12- ...original ,
13- wrapper : {
14- exit : jest . fn ( ) ,
15- } ,
16- }
17- } )
18-
198describe ( "Heart" , ( ) => {
209 const testName = "heartTests"
2110 let testDir = ""
@@ -27,7 +16,7 @@ describe("Heart", () => {
2716 testDir = await tmpdir ( testName )
2817 } )
2918 beforeEach ( ( ) => {
30- heart = new Heart ( `${ testDir } /shutdown.txt` , undefined , mockIsActive ( true ) )
19+ heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive ( true ) )
3120 } )
3221 afterAll ( ( ) => {
3322 jest . restoreAllMocks ( )
@@ -53,7 +42,7 @@ describe("Heart", () => {
5342
5443 expect ( fileContents ) . toBe ( text )
5544
56- heart = new Heart ( pathToFile , undefined , mockIsActive ( true ) )
45+ heart = new Heart ( pathToFile , mockIsActive ( true ) )
5746 await heart . beat ( )
5847 // Check that the heart wrote to the heartbeatFilePath and overwrote our text
5948 const fileContentsAfterBeat = await readFile ( pathToFile , { encoding : "utf8" } )
@@ -63,7 +52,7 @@ describe("Heart", () => {
6352 expect ( fileStatusAfterEdit . mtimeMs ) . toBeGreaterThan ( 0 )
6453 } )
6554 it ( "should log a warning when given an invalid file path" , async ( ) => {
66- heart = new Heart ( `fakeDir/fake.txt` , undefined , mockIsActive ( false ) )
55+ heart = new Heart ( `fakeDir/fake.txt` , mockIsActive ( false ) )
6756 await heart . beat ( )
6857 expect ( logger . warn ) . toHaveBeenCalled ( )
6958 } )
@@ -82,7 +71,7 @@ describe("Heart", () => {
8271 it ( "should beat twice without warnings" , async ( ) => {
8372 // Use fake timers so we can speed up setTimeout
8473 jest . useFakeTimers ( )
85- heart = new Heart ( `${ testDir } /hello.txt` , undefined , mockIsActive ( true ) )
74+ heart = new Heart ( `${ testDir } /hello.txt` , mockIsActive ( true ) )
8675 await heart . beat ( )
8776 // we need to speed up clocks, timeouts
8877 // call heartbeat again (and it won't be alive I think)
@@ -93,37 +82,47 @@ describe("Heart", () => {
9382} )
9483
9584describe ( "heartbeatTimer" , ( ) => {
96- beforeAll ( ( ) => {
85+ const testName = "heartbeatTimer"
86+ let testDir = ""
87+ beforeAll ( async ( ) => {
88+ await clean ( testName )
89+ testDir = await tmpdir ( testName )
9790 mockLogger ( )
9891 } )
9992 afterAll ( ( ) => {
10093 jest . restoreAllMocks ( )
10194 } )
95+ beforeEach ( ( ) => {
96+ jest . useFakeTimers ( )
97+ } )
10298 afterEach ( ( ) => {
10399 jest . resetAllMocks ( )
100+ jest . clearAllTimers ( )
101+ jest . useRealTimers ( )
104102 } )
105- it ( "should call beat when isActive resolves to true " , async ( ) => {
103+ it ( "should call isActive when timeout expires " , async ( ) => {
106104 const isActive = true
107105 const mockIsActive = jest . fn ( ) . mockResolvedValue ( isActive )
108- const mockBeatFn = jest . fn ( )
109- await heartbeatTimer ( mockIsActive , mockBeatFn )
106+ const heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive )
107+ await heart . beat ( )
108+ jest . advanceTimersByTime ( 60 * 1000 )
110109 expect ( mockIsActive ) . toHaveBeenCalled ( )
111- expect ( mockBeatFn ) . toHaveBeenCalled ( )
112110 } )
113111 it ( "should log a warning when isActive rejects" , async ( ) => {
114112 const errorMsg = "oh no"
115113 const error = new Error ( errorMsg )
116114 const mockIsActive = jest . fn ( ) . mockRejectedValue ( error )
117- const mockBeatFn = jest . fn ( )
118- await heartbeatTimer ( mockIsActive , mockBeatFn )
115+ const heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive )
116+ await heart . beat ( )
117+ jest . advanceTimersByTime ( 60 * 1000 )
118+
119119 expect ( mockIsActive ) . toHaveBeenCalled ( )
120- expect ( mockBeatFn ) . not . toHaveBeenCalled ( )
121120 expect ( logger . warn ) . toHaveBeenCalledWith ( errorMsg )
122121 } )
123122} )
124123
125- describe ( "idleTimeout " , ( ) => {
126- const testName = "idleHeartTests "
124+ describe ( "stateChange " , ( ) => {
125+ const testName = "stateChange "
127126 let testDir = ""
128127 let heart : Heart
129128 beforeAll ( async ( ) => {
@@ -140,12 +139,23 @@ describe("idleTimeout", () => {
140139 heart . dispose ( )
141140 }
142141 } )
143- it ( "should call beat when isActive resolves to true" , async ( ) => {
142+ it ( "should change to alive after a beat" , async ( ) => {
143+ heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive ( true ) )
144+ const mockOnChange = jest . fn ( )
145+ heart . onChange ( mockOnChange )
146+ await heart . beat ( )
147+
148+ expect ( mockOnChange . mock . calls [ 0 ] [ 0 ] ) . toBe ( "alive" )
149+ } )
150+ it . only ( "should change to idle when not active" , async ( ) => {
144151 jest . useFakeTimers ( )
145- heart = new Heart ( `${ testDir } /shutdown.txt` , 60 , mockIsActive ( true ) )
152+ heart = new Heart ( `${ testDir } /shutdown.txt` , ( ) => new Promise ( ( resolve ) => resolve ( false ) ) )
153+ const mockOnChange = jest . fn ( )
154+ heart . onChange ( mockOnChange )
155+ await heart . beat ( )
146156
147- jest . advanceTimersByTime ( 60 * 1000 )
148- expect ( wrapper . exit ) . toHaveBeenCalled ( )
157+ await jest . advanceTimersByTime ( 60 * 1000 )
158+ expect ( mockOnChange . mock . calls [ 1 ] [ 0 ] ) . toBe ( "idle" )
149159 jest . clearAllTimers ( )
150160 jest . useRealTimers ( )
151161 } )
0 commit comments