Skip to content

Commit 38981a4

Browse files
committed
test: adjust test for vitest 4
1 parent ad1c6e5 commit 38981a4

File tree

13 files changed

+244
-58
lines changed

13 files changed

+244
-58
lines changed

__mocks__/fs.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Mock for node:fs using memfs
2+
// See: https://vitest.dev/guide/mocking/file-system.html
3+
4+
const { fs } = require('memfs')
5+
module.exports = fs
6+

__mocks__/fs/promises.cjs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Mock for node:fs/promises using memfs
2+
// See: https://vitest.dev/guide/mocking/file-system.html
3+
4+
const { fs } = require('memfs')
5+
module.exports = fs.promises
6+

main/src/tests/auto-update.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ vi.mock('electron-store', () => {
104104
}
105105

106106
return {
107-
default: vi.fn().mockImplementation(() => mockStoreInstance),
107+
default: vi.fn(function ElectronStore() {
108+
return mockStoreInstance
109+
}),
108110
}
109111
})
110112

main/src/tests/graceful-exit.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ vi.mock('electron-store', () => {
4848
).__mockStoreInstance = mockStoreInstance
4949

5050
return {
51-
default: vi.fn().mockImplementation(() => mockStoreInstance),
51+
default: vi.fn(function ElectronStore() {
52+
return mockStoreInstance
53+
}),
5254
}
5355
})
5456

main/src/tests/toolhive-manager.test.ts

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,48 @@
11
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2+
import { EventEmitter } from 'node:events'
3+
import { vol } from 'memfs'
24
import { spawn } from 'node:child_process'
3-
import { existsSync } from 'node:fs'
45
import net from 'node:net'
56
import { app } from 'electron'
6-
import { EventEmitter } from 'node:events'
77
import {
88
startToolhive,
99
getToolhivePort,
1010
getToolhiveMcpPort,
1111
isToolhiveRunning,
1212
stopToolhive,
1313
restartToolhive,
14+
binPath,
1415
} from '../toolhive-manager'
1516
import { updateTrayStatus } from '../system-tray'
1617
import log from '../logger'
1718
import * as Sentry from '@sentry/electron/main'
1819
import { getQuittingState } from '../app-state'
1920

20-
// Mock dependencies
21-
vi.mock('node:child_process')
21+
vi.mock('node:child_process', async (importOriginal) => {
22+
const actual = await importOriginal<typeof import('node:child_process')>()
23+
const mockSpawnFn = vi.fn()
24+
return {
25+
...actual,
26+
spawn: mockSpawnFn,
27+
default: {
28+
...actual,
29+
spawn: mockSpawnFn,
30+
},
31+
}
32+
})
2233
vi.mock('node:fs')
23-
vi.mock('node:net')
34+
vi.mock('node:net', async (importOriginal) => {
35+
const actual = await importOriginal<typeof import('node:net')>()
36+
const mockCreateServerFn = vi.fn()
37+
return {
38+
...actual,
39+
createServer: mockCreateServerFn,
40+
default: {
41+
...actual,
42+
createServer: mockCreateServerFn,
43+
},
44+
}
45+
})
2446
vi.mock('electron', () => ({
2547
app: {
2648
isPackaged: false,
@@ -53,12 +75,14 @@ vi.mock('electron-store', () => {
5375
}
5476

5577
return {
56-
default: vi.fn().mockImplementation(() => mockStoreInstance),
78+
default: vi.fn(function ElectronStore() {
79+
return mockStoreInstance
80+
}),
5781
}
5882
})
5983

84+
// Get mocked functions
6085
const mockSpawn = vi.mocked(spawn)
61-
const mockExistsSync = vi.mocked(existsSync)
6286
const mockNet = vi.mocked(net)
6387
const mockApp = vi.mocked(app)
6488
const mockUpdateTrayStatus = vi.mocked(updateTrayStatus)
@@ -112,26 +136,35 @@ describe('toolhive-manager', () => {
112136
vi.clearAllMocks()
113137
vi.useFakeTimers()
114138

139+
// Reset the in-memory file system
140+
vol.reset()
141+
142+
// Setup the file system state - make binary exist by default
143+
vol.fromJSON({
144+
[binPath]: '', // Mock the binary file exists
145+
})
146+
115147
// Reset module state
116148
stopToolhive()
117149

118150
// Setup mocks
119151
mockProcess = new MockProcess()
120152

121-
mockSpawn.mockReturnValue(
122-
mockProcess as unknown as ReturnType<typeof spawn>
123-
)
124-
mockExistsSync.mockReturnValue(true)
153+
mockSpawn.mockImplementation(function spawn() {
154+
return mockProcess as unknown as ReturnType<
155+
typeof import('node:child_process').spawn
156+
>
157+
})
125158
Object.defineProperty(mockApp, 'isPackaged', {
126159
value: false,
127160
configurable: true,
128161
})
129162
vi.mocked(mockApp.getPath).mockReturnValue('/test/userData')
130163

131164
// Mock net.createServer to return a new MockServer instance each time
132-
mockNet.createServer.mockImplementation(
133-
() => new MockServer() as unknown as net.Server
134-
)
165+
mockNet.createServer.mockImplementation(function createServer() {
166+
return new MockServer() as unknown as net.Server
167+
})
135168

136169
// Mock logger methods
137170
mockLog.info = vi.fn()
@@ -147,7 +180,8 @@ describe('toolhive-manager', () => {
147180

148181
describe('startToolhive', () => {
149182
it('returns early if binary does not exist', async () => {
150-
mockExistsSync.mockReturnValue(false)
183+
// Remove the binary file from the in-memory file system
184+
vol.reset()
151185

152186
await startToolhive()
153187

@@ -382,28 +416,30 @@ describe('toolhive-manager', () => {
382416
describe('port finding with fallback', () => {
383417
it('falls back to random port when preferred range is unavailable', async () => {
384418
// Mock all ports in range to be unavailable, then allow random port
385-
mockNet.createServer.mockImplementation(() => {
419+
mockNet.createServer.mockImplementation(function createServer() {
386420
const server = new MockServer() as unknown as net.Server
387421
const originalListen = server.listen.bind(server)
388422

389-
server.listen = vi
390-
.fn()
391-
.mockImplementation((port: number, callback?: () => void) => {
392-
if (port >= 50000 && port <= 50100) {
393-
// Simulate all ports in range being unavailable
394-
setTimeout(() => {
395-
server.emit('error', { code: 'EADDRINUSE' })
396-
}, 5)
397-
} else if (port === 0) {
398-
// Allow OS assignment (fallback)
399-
setTimeout(() => {
400-
originalListen(port, callback)
401-
}, 5)
402-
} else {
403-
// Any other specific port
423+
server.listen = vi.fn(function listen(
424+
port: number,
425+
callback?: () => void
426+
) {
427+
if (port >= 50000 && port <= 50100) {
428+
// Simulate all ports in range being unavailable
429+
setTimeout(() => {
430+
server.emit('error', { code: 'EADDRINUSE' })
431+
}, 5)
432+
} else if (port === 0) {
433+
// Allow OS assignment (fallback)
434+
setTimeout(() => {
404435
originalListen(port, callback)
405-
}
406-
})
436+
}, 5)
437+
} else {
438+
// Any other specific port
439+
originalListen(port, callback)
440+
}
441+
return server
442+
}) as unknown as typeof server.listen
407443

408444
return server
409445
})
@@ -566,10 +602,12 @@ describe('toolhive-manager', () => {
566602

567603
it('handles kill errors and attempts force kill as fallback', () => {
568604
const killSpy = vi.spyOn(mockProcess, 'kill')
569-
killSpy.mockImplementationOnce(() => {
605+
killSpy.mockImplementationOnce(function killImpl() {
570606
throw new Error('Kill failed')
571607
})
572-
killSpy.mockImplementationOnce(() => true)
608+
killSpy.mockImplementationOnce(function killImpl() {
609+
return true
610+
})
573611

574612
stopToolhive()
575613

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
"json-schema-faker": "^0.5.9",
7777
"knip": "^5.58.1",
7878
"lint-staged": "^16.0.0",
79+
"memfs": "^4.50.0",
7980
"msw": "^2.8.4",
8081
"prettier": "3.6.2",
8182
"prettier-plugin-classnames": "^0.8.0",

0 commit comments

Comments
 (0)