cli: fix type errors and test for embedded-postgres

Fix implicit any types in startEmbeddedDb callbacks, replace the
re-export type declaration with an inline type definition for the
embedded-postgres module, and update runBackend tests to mock the
config loading and use async timer advancement.

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
Made-with: Cursor
This commit is contained in:
Patrik Oldsberg
2026-04-01 15:40:31 +02:00
parent 538d0a1488
commit 6537e5f8c1
3 changed files with 55 additions and 42 deletions
@@ -49,6 +49,24 @@ jest.mock('ctrlc-windows', () => ({
ctrlc: jest.fn(),
}));
jest.mock('@backstage/config-loader', () => ({
ConfigSources: {
default: () => ({
readConfigData: async function* readConfigData() {
yield { configs: [] };
},
}),
},
}));
jest.mock('@backstage/config', () => ({
ConfigReader: {
fromConfigs: () => ({
getOptionalString: () => undefined,
}),
},
}));
describe('runBackend', () => {
let originalEnv: NodeJS.ProcessEnv;
let originalPlatform: string;
@@ -82,92 +100,73 @@ describe('runBackend', () => {
});
describe('--no-node-snapshot argument handling', () => {
it('should pass --no-node-snapshot when NODE_OPTIONS is not set', () => {
it('should pass --no-node-snapshot when NODE_OPTIONS is not set', async () => {
delete process.env.NODE_OPTIONS;
runBackend({
entry: 'src/index',
});
runBackend({ entry: 'src/index' });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
expect(spawnArgs).toContain('--no-node-snapshot');
});
it('should pass --no-node-snapshot when NODE_OPTIONS exists without --node-snapshot', () => {
it('should pass --no-node-snapshot when NODE_OPTIONS exists without --node-snapshot', async () => {
process.env.NODE_OPTIONS = '--max-old-space-size=4096';
runBackend({
entry: 'src/index',
});
runBackend({ entry: 'src/index' });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
expect(spawnArgs).toContain('--no-node-snapshot');
});
it('should not pass --no-node-snapshot when --node-snapshot already exists in NODE_OPTIONS', () => {
it('should not pass --no-node-snapshot when --node-snapshot already exists in NODE_OPTIONS', async () => {
process.env.NODE_OPTIONS = '--node-snapshot --max-old-space-size=4096';
runBackend({
entry: 'src/index',
});
runBackend({ entry: 'src/index' });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
expect(spawnArgs).not.toContain('--no-node-snapshot');
});
it('should not pass --no-node-snapshot when --node-snapshot exists in the middle of NODE_OPTIONS', () => {
it('should not pass --no-node-snapshot when --node-snapshot exists in the middle of NODE_OPTIONS', async () => {
process.env.NODE_OPTIONS =
'--max-old-space-size=4096 --node-snapshot --inspect';
runBackend({
entry: 'src/index',
});
runBackend({ entry: 'src/index' });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
expect(spawnArgs).not.toContain('--no-node-snapshot');
});
it('should pass --no-node-snapshot even with trailing spaces in NODE_OPTIONS', () => {
it('should pass --no-node-snapshot even with trailing spaces in NODE_OPTIONS', async () => {
process.env.NODE_OPTIONS = '--max-old-space-size=4096 ';
runBackend({
entry: 'src/index',
});
runBackend({ entry: 'src/index' });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
expect(spawnArgs).toContain('--no-node-snapshot');
});
it('should pass --no-node-snapshot alongside other option args like --inspect', () => {
it('should pass --no-node-snapshot alongside other option args like --inspect', async () => {
delete process.env.NODE_OPTIONS;
runBackend({
entry: 'src/index',
inspectEnabled: true,
});
runBackend({ entry: 'src/index', inspectEnabled: true });
// Fast-forward past the debounce delay (100ms)
jest.advanceTimersByTime(100);
await jest.advanceTimersByTimeAsync(100);
expect(mockSpawn).toHaveBeenCalled();
const spawnArgs = mockSpawn.mock.calls[0][1] as string[];
@@ -16,7 +16,7 @@
import os from 'node:os';
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { resolve as resolvePath } from 'node:path';
import { getPortPromise } from 'portfinder';
export async function startEmbeddedDb() {
@@ -42,8 +42,8 @@ export async function startEmbeddedDb() {
password,
port,
persistent: false,
onError(_messageOrError) {},
onLog(_message) {},
onError(_messageOrError: unknown) {},
onLog(_message: unknown) {},
});
// Create the cluster config files
+16 -2
View File
@@ -14,7 +14,21 @@
* limitations under the License.
*/
// It's missing a types entry point, but has types in dist
declare module 'embedded-postgres' {
export { default } from 'embedded-postgres/dist/index';
export interface EmbeddedPostgresOptions {
databaseDir: string;
user: string;
password: string;
port: number;
persistent: boolean;
onError?: (messageOrError: unknown) => void;
onLog?: (message: unknown) => void;
}
export default class EmbeddedPostgres {
constructor(options: EmbeddedPostgresOptions);
initialise(): Promise<void>;
start(): Promise<void>;
stop(): Promise<void>;
}
}