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 )
@@ -27,7 +26,7 @@ describe("Heart", () => {
2726 testDir = await tmpdir ( testName )
2827 } )
2928 beforeEach ( ( ) => {
30- heart = new Heart ( `${ testDir } /shutdown.txt` , undefined , mockIsActive ( true ) )
29+ heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive ( true ) )
3130 } )
3231 afterAll ( ( ) => {
3332 jest . restoreAllMocks ( )
@@ -53,7 +52,7 @@ describe("Heart", () => {
5352
5453 expect ( fileContents ) . toBe ( text )
5554
56- heart = new Heart ( pathToFile , undefined , mockIsActive ( true ) )
55+ heart = new Heart ( pathToFile , mockIsActive ( true ) )
5756 await heart . beat ( )
5857 // Check that the heart wrote to the heartbeatFilePath and overwrote our text
5958 const fileContentsAfterBeat = await readFile ( pathToFile , { encoding : "utf8" } )
@@ -63,7 +62,7 @@ describe("Heart", () => {
6362 expect ( fileStatusAfterEdit . mtimeMs ) . toBeGreaterThan ( 0 )
6463 } )
6564 it ( "should log a warning when given an invalid file path" , async ( ) => {
66- heart = new Heart ( `fakeDir/fake.txt` , undefined , mockIsActive ( false ) )
65+ heart = new Heart ( `fakeDir/fake.txt` , mockIsActive ( false ) )
6766 await heart . beat ( )
6867 expect ( logger . warn ) . toHaveBeenCalled ( )
6968 } )
@@ -82,7 +81,7 @@ describe("Heart", () => {
8281 it ( "should beat twice without warnings" , async ( ) => {
8382 // Use fake timers so we can speed up setTimeout
8483 jest . useFakeTimers ( )
85- heart = new Heart ( `${ testDir } /hello.txt` , undefined , mockIsActive ( true ) )
84+ heart = new Heart ( `${ testDir } /hello.txt` , mockIsActive ( true ) )
8685 await heart . beat ( )
8786 // we need to speed up clocks, timeouts
8887 // call heartbeat again (and it won't be alive I think)
@@ -93,37 +92,47 @@ describe("Heart", () => {
9392} )
9493
9594describe ( "heartbeatTimer" , ( ) => {
96- beforeAll ( ( ) => {
95+ const testName = "heartbeatTimer"
96+ let testDir = ""
97+ beforeAll ( async ( ) => {
98+ await clean ( testName )
99+ testDir = await tmpdir ( testName )
97100 mockLogger ( )
98101 } )
99102 afterAll ( ( ) => {
100103 jest . restoreAllMocks ( )
101104 } )
105+ beforeEach ( ( ) => {
106+ jest . useFakeTimers ( )
107+ } )
102108 afterEach ( ( ) => {
103109 jest . resetAllMocks ( )
110+ jest . clearAllTimers ( )
111+ jest . useRealTimers ( )
104112 } )
105- it ( "should call beat when isActive resolves to true " , async ( ) => {
113+ it ( "should call isActive when timeout expires " , async ( ) => {
106114 const isActive = true
107115 const mockIsActive = jest . fn ( ) . mockResolvedValue ( isActive )
108- const mockBeatFn = jest . fn ( )
109- await heartbeatTimer ( mockIsActive , mockBeatFn )
116+ const heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive )
117+ await heart . beat ( )
118+ jest . advanceTimersByTime ( 60 * 1000 )
110119 expect ( mockIsActive ) . toHaveBeenCalled ( )
111- expect ( mockBeatFn ) . toHaveBeenCalled ( )
112120 } )
113121 it ( "should log a warning when isActive rejects" , async ( ) => {
114122 const errorMsg = "oh no"
115123 const error = new Error ( errorMsg )
116124 const mockIsActive = jest . fn ( ) . mockRejectedValue ( error )
117- const mockBeatFn = jest . fn ( )
118- await heartbeatTimer ( mockIsActive , mockBeatFn )
125+ const heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive )
126+ await heart . beat ( )
127+ jest . advanceTimersByTime ( 60 * 1000 )
128+
119129 expect ( mockIsActive ) . toHaveBeenCalled ( )
120- expect ( mockBeatFn ) . not . toHaveBeenCalled ( )
121130 expect ( logger . warn ) . toHaveBeenCalledWith ( errorMsg )
122131 } )
123132} )
124133
125- describe ( "idleTimeout " , ( ) => {
126- const testName = "idleHeartTests "
134+ describe ( "stateChange " , ( ) => {
135+ const testName = "stateChange "
127136 let testDir = ""
128137 let heart : Heart
129138 beforeAll ( async ( ) => {
@@ -140,12 +149,23 @@ describe("idleTimeout", () => {
140149 heart . dispose ( )
141150 }
142151 } )
143- it ( "should call beat when isActive resolves to true" , async ( ) => {
152+ it ( "should change to alive after a beat" , async ( ) => {
153+ heart = new Heart ( `${ testDir } /shutdown.txt` , mockIsActive ( true ) )
154+ const mockOnChange = jest . fn ( )
155+ heart . onChange ( mockOnChange )
156+ await heart . beat ( )
157+
158+ expect ( mockOnChange . mock . calls [ 0 ] [ 0 ] ) . toBe ( "alive" )
159+ } )
160+ it . only ( "should change to idle when not active" , async ( ) => {
144161 jest . useFakeTimers ( )
145- heart = new Heart ( `${ testDir } /shutdown.txt` , 60 , mockIsActive ( true ) )
162+ heart = new Heart ( `${ testDir } /shutdown.txt` , ( ) => new Promise ( ( resolve ) => resolve ( false ) ) )
163+ const mockOnChange = jest . fn ( )
164+ heart . onChange ( mockOnChange )
165+ await heart . beat ( )
146166
147- jest . advanceTimersByTime ( 60 * 1000 )
148- expect ( wrapper . exit ) . toHaveBeenCalled ( )
167+ await jest . advanceTimersByTime ( 60 * 1000 )
168+ expect ( mockOnChange . mock . calls [ 1 ] [ 0 ] ) . toBe ( "idle" )
149169 jest . clearAllTimers ( )
150170 jest . useRealTimers ( )
151171 } )
0 commit comments