mirror of
https://github.com/davidkaya/aryx.git
synced 2026-08-26 21:03:58 +02:00
fix: handle bun child_process interop
Resolve execFile from named or default child_process exports so macOS Bun can load gitService in tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,32 @@
|
|||||||
import childProcess, { type ExecFileException } from 'node:child_process';
|
import * as childProcessModule from 'node:child_process';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
|
|
||||||
import type { ProjectGitChangeSummary, ProjectGitCommitSummary, ProjectGitContext } from '@shared/domain/project';
|
import type { ProjectGitChangeSummary, ProjectGitCommitSummary, ProjectGitContext } from '@shared/domain/project';
|
||||||
import { nowIso } from '@shared/utils/ids';
|
import { nowIso } from '@shared/utils/ids';
|
||||||
|
|
||||||
const execFileAsync = promisify(childProcess.execFile);
|
type ExecFileException = import('node:child_process').ExecFileException;
|
||||||
|
|
||||||
|
type ChildProcessModuleLike = {
|
||||||
|
readonly execFile?: typeof childProcessModule.execFile;
|
||||||
|
readonly default?: {
|
||||||
|
readonly execFile?: typeof childProcessModule.execFile;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function resolveExecFileForInterop(
|
||||||
|
module: ChildProcessModuleLike,
|
||||||
|
): typeof childProcessModule.execFile {
|
||||||
|
const execFile = module.execFile ?? module.default?.execFile;
|
||||||
|
if (typeof execFile !== 'function') {
|
||||||
|
throw new Error('node:child_process execFile is unavailable.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return execFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
const execFileAsync = promisify(
|
||||||
|
resolveExecFileForInterop(childProcessModule as ChildProcessModuleLike),
|
||||||
|
);
|
||||||
const GIT_TIMEOUT_MS = 5_000;
|
const GIT_TIMEOUT_MS = 5_000;
|
||||||
|
|
||||||
type GitCommandRunner = (projectPath: string, args: string[]) => Promise<string>;
|
type GitCommandRunner = (projectPath: string, args: string[]) => Promise<string>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, test } from 'bun:test';
|
import { describe, expect, test } from 'bun:test';
|
||||||
|
|
||||||
import { GitService } from '@main/git/gitService';
|
import { GitService, resolveExecFileForInterop } from '@main/git/gitService';
|
||||||
|
|
||||||
function createGitError(message: string, options?: { code?: number | string; stderr?: string }) {
|
function createGitError(message: string, options?: { code?: number | string; stderr?: string }) {
|
||||||
return Object.assign(new Error(message), options);
|
return Object.assign(new Error(message), options);
|
||||||
@@ -24,6 +24,24 @@ function createService(responses: Record<string, string | Error>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
describe('GitService', () => {
|
describe('GitService', () => {
|
||||||
|
test('resolves execFile from the named child_process export when available', () => {
|
||||||
|
const namedExecFile = (() => undefined) as unknown as typeof import('node:child_process').execFile;
|
||||||
|
|
||||||
|
expect(resolveExecFileForInterop({ execFile: namedExecFile })).toBe(namedExecFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('falls back to default.execFile for Bun child_process interop', () => {
|
||||||
|
const defaultExecFile = (() => undefined) as unknown as typeof import('node:child_process').execFile;
|
||||||
|
|
||||||
|
expect(resolveExecFileForInterop({ default: { execFile: defaultExecFile } })).toBe(defaultExecFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throws when execFile is unavailable in both child_process export shapes', () => {
|
||||||
|
expect(() => resolveExecFileForInterop({})).toThrow(
|
||||||
|
'node:child_process execFile is unavailable.',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('describes a git repository with branch, dirty state, change counts, and head commit summary', async () => {
|
test('describes a git repository with branch, dirty state, change counts, and head commit summary', async () => {
|
||||||
const service = createService({
|
const service = createService({
|
||||||
'rev-parse --show-toplevel': 'C:\\workspace\\repo\n',
|
'rev-parse --show-toplevel': 'C:\\workspace\\repo\n',
|
||||||
|
|||||||
Reference in New Issue
Block a user