-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.test.ts
More file actions
141 lines (118 loc) · 5.62 KB
/
main.test.ts
File metadata and controls
141 lines (118 loc) · 5.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import 'reflect-metadata';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
/*
* Declare spy variables via vi.hoisted() so they are initialised before the
* vi.mock() factory functions run (vi.mock calls are hoisted to the top of the
* compiled output, above regular const/let declarations).
*/
const { ElectronIPCTransportMock, fakeApp, createMicroserviceMock, applyFixPathMock, mockApp, createLoggerMock } = vi.hoisted(() => {
/** Fake NestJS microservice app returned by `NestFactory.createMicroservice`. */
const fakeApp = { listen: vi.fn().mockResolvedValue(undefined) };
/** Spy constructor for ElectronIPCTransport — tracks `new` invocations. */
const ElectronIPCTransportMock = vi.fn().mockImplementation(() => ({}));
/** Spy for `NestFactory.createMicroservice`. */
const createMicroserviceMock = vi.fn().mockResolvedValue(fakeApp);
/** Spy for `applyFixPath` — verifies the fix-path bootstrap is called during startup. */
const applyFixPathMock = vi.fn();
/** Fake Electron `app` object with a `getPath` spy. */
const mockApp = { getPath: vi.fn().mockReturnValue('/fake/userData') };
/** Spy for `createLogger` — verifies the file logger is initialised before bootstrap. */
const createLoggerMock = vi.fn();
return { ElectronIPCTransportMock, fakeApp, createMicroserviceMock, applyFixPathMock, mockApp, createLoggerMock };
});
vi.mock('electron', () => ({
app: mockApp,
}));
vi.mock('./logger.js', () => ({
createLogger: createLoggerMock,
}));
vi.mock('nestjs-electron-ipc-transport', () => ({
ElectronIPCTransport: ElectronIPCTransportMock,
}));
vi.mock('@nestjs/core', () => ({
NestFactory: {
createMicroservice: createMicroserviceMock,
},
}));
/**
* Stub AppModule so the deep Nest module graph (which requires the generated
* tfstate file and AWS service dependencies) is never traversed during this
* unit test.
*/
vi.mock('./app.module.js', () => ({
AppModule: class AppModule {},
}));
vi.mock('./fix-path-bootstrap.js', () => ({
applyFixPath: applyFixPathMock,
}));
describe('main bootstrap', () => {
beforeEach(() => {
/*
* clearMocks: true (in vitest.config.ts) clears mock implementations before
* each test. Re-apply the return values here so they are set when main.ts
* executes during the test body.
*/
ElectronIPCTransportMock.mockImplementation(() => ({}));
createMicroserviceMock.mockResolvedValue(fakeApp);
fakeApp.listen.mockResolvedValue(undefined);
applyFixPathMock.mockImplementation(() => undefined);
mockApp.getPath.mockReturnValue('/fake/userData');
createLoggerMock.mockImplementation(() => undefined);
// Simulate an Electron main-process environment so the module-level guard passes.
vi.stubGlobal('process', { ...process, versions: { ...process.versions, electron: '36.0.0' } });
});
afterEach(() => {
vi.unstubAllGlobals();
});
it('should throw a clear error when not running inside an Electron main process', async () => {
vi.stubGlobal('process', { ...process, versions: {} });
vi.resetModules();
await expect(import('./main.js')).rejects.toThrow('Electron main process');
});
it('should bootstrap as a NestJS microservice using ElectronIPCTransport', async () => {
/*
* Reset the module registry so re-importing main.ts forces the module to
* re-execute (and thus `void bootstrap()` fires again) after clearMocks has
* reset spy counters. vi.mock() registrations survive vi.resetModules(), so
* all stubs remain active.
*/
vi.resetModules();
await import('./main.js');
// Flush the event loop so the async bootstrap chain fully resolves.
await new Promise<void>((resolve) => setTimeout(resolve, 0));
const { AppModule } = await import('./app.module.js');
// ElectronIPCTransport should have been constructed with `new`.
expect(ElectronIPCTransportMock).toHaveBeenCalledOnce();
// createMicroservice should have been called with AppModule and a strategy option.
expect(createMicroserviceMock).toHaveBeenCalledOnce();
const [calledModule, calledOptions] = createMicroserviceMock.mock.calls[0] as [
unknown,
{ strategy: unknown },
];
expect(calledModule).toBe(AppModule);
expect(calledOptions).toHaveProperty('strategy');
// The strategy passed to createMicroservice should be the value returned by
// `new ElectronIPCTransport()` — i.e. the object the mock constructor produced.
expect(calledOptions.strategy).toBe(ElectronIPCTransportMock.mock.results[0].value);
// listen() should have been called on the fake app.
expect(fakeApp.listen).toHaveBeenCalledOnce();
});
it('should call applyFixPath during bootstrap', async () => {
vi.resetModules();
await import('./main.js');
// Flush the event loop so the async bootstrap chain fully resolves.
await new Promise<void>((resolve) => setTimeout(resolve, 0));
// applyFixPath must be invoked exactly once before NestFactory.createMicroservice.
expect(applyFixPathMock).toHaveBeenCalledTimes(1);
});
it('should resolve userData path and initialise the file logger before bootstrap', async () => {
vi.resetModules();
await import('./main.js');
// Flush the event loop so the async bootstrap chain fully resolves.
await new Promise<void>((resolve) => setTimeout(resolve, 0));
// Electron app.getPath should have been called with 'userData' to derive the log directory.
expect(mockApp.getPath).toHaveBeenCalledWith('userData');
// createLogger must have been called with the userData/logs path before NestFactory boots.
expect(createLoggerMock).toHaveBeenCalledWith('/fake/userData/logs');
});
});