Skip to content

Commit 3934ca3

Browse files
author
Joe Previte
committed
feat(testing): add tests for heart.ts
1 parent bd3a0cb commit 3934ca3

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

test/unit/node/heart.test.ts

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { readFile, writeFile } from "fs/promises"
2+
import { logger } from "@coder/logger"
3+
import { Heart, heartbeatTimer } from "../../../src/node/heart"
4+
import { clean, mockLogger, tmpdir } from "../../utils/helpers"
5+
6+
function mockIsActive(resolveTo: boolean): () => Promise<boolean> {
7+
return () => new Promise((resolve, reject) => setTimeout(() => resolve(resolveTo), 100))
8+
}
9+
10+
describe("Heart", () => {
11+
const testName = "heartTests"
12+
let testDir = ""
13+
let heart: Heart
14+
15+
beforeAll(async () => {
16+
mockLogger()
17+
await clean(testName)
18+
testDir = await tmpdir(testName)
19+
})
20+
beforeEach(() => {
21+
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true))
22+
})
23+
afterAll(() => {
24+
jest.restoreAllMocks()
25+
})
26+
afterEach(() => {
27+
jest.resetAllMocks()
28+
if (heart) {
29+
heart.dispose()
30+
}
31+
})
32+
it("should write to a file when given a valid file path", async () => {
33+
// Set up heartbeat file with contents
34+
const text = "test"
35+
const pathToFile = `${testDir}/file.txt`
36+
await writeFile(pathToFile, text)
37+
const fileContents = await readFile(pathToFile, { encoding: "utf8" })
38+
expect(fileContents).toBe(text)
39+
40+
heart = new Heart(pathToFile, mockIsActive(true))
41+
heart.beat()
42+
// Check that the heart wrote to the heartbeatFilePath and overwrote our text
43+
const fileContentsAfterBeat = await readFile(pathToFile, { encoding: "utf8" })
44+
expect(fileContentsAfterBeat).not.toBe(text)
45+
})
46+
it("should log a warning when given an invalid file path", async () => {
47+
heart = new Heart(`fakeDir/fake.txt`, mockIsActive(false))
48+
heart.beat()
49+
// HACK@jsjoeio - beat has some async logic but is not an async method
50+
// Therefore, we have to create an artificial wait in order to make sure
51+
// all async code has completed before asserting
52+
await new Promise((r) => setTimeout(r, 100))
53+
// expect(logger.trace).toHaveBeenCalled()
54+
expect(logger.warn).toHaveBeenCalled()
55+
})
56+
it("should be active after calling beat", () => {
57+
heart.beat()
58+
59+
const isAlive = heart.alive()
60+
expect(isAlive).toBe(true)
61+
})
62+
it("should not be active after dispose is called", () => {
63+
heart = new Heart(`${testDir}/shutdown.txt`, mockIsActive(true))
64+
heart.dispose()
65+
66+
const isAlive = heart.alive()
67+
expect(isAlive).toBe(false)
68+
})
69+
})
70+
71+
describe("heartbeatTimer", () => {
72+
beforeAll(() => {
73+
mockLogger()
74+
})
75+
afterAll(() => {
76+
jest.restoreAllMocks()
77+
})
78+
afterEach(() => {
79+
jest.resetAllMocks()
80+
})
81+
it("should call beat when is active resolves to true", async () => {
82+
const isActive = true
83+
const mockIsActive = jest.fn().mockResolvedValue(isActive)
84+
const mockBeatFn = jest.fn()
85+
await heartbeatTimer(mockIsActive, mockBeatFn)
86+
expect(mockIsActive).toHaveBeenCalled()
87+
expect(mockBeatFn).toHaveBeenCalled()
88+
})
89+
it("should log a warning when is active rejects", async () => {
90+
const errorMsg = "oh no"
91+
const error = new Error(errorMsg)
92+
const mockIsActive = jest.fn().mockRejectedValue(error)
93+
const mockBeatFn = jest.fn()
94+
await heartbeatTimer(mockIsActive, mockBeatFn)
95+
expect(mockIsActive).toHaveBeenCalled()
96+
expect(mockBeatFn).not.toHaveBeenCalled()
97+
expect(logger.warn).toHaveBeenCalledWith(errorMsg)
98+
})
99+
})

0 commit comments

Comments
 (0)