Skip to content

test(node): Ensure runner does not create unused tmp files #17392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 12, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 67 additions & 57 deletions dev-packages/node-integration-tests/utils/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { execSync, spawn, spawnSync } from 'child_process';
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { basename, join } from 'path';
import { inspect } from 'util';
import { afterAll, describe, test } from 'vitest';
import { afterAll, beforeAll, describe, test } from 'vitest';
import {
assertEnvelopeHeader,
assertSentryCheckIn,
Expand Down Expand Up @@ -195,74 +195,79 @@ export function createEsmAndCjsTests(
// If additionalDependencies are provided, we also create a nested package.json and install them there.
const uniqueId = `${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
const tmpDirPath = join(cwd, `tmp_${uniqueId}`);
mkdirSync(tmpDirPath);

// Copy ESM files as-is into tmp dir
const esmScenarioBasename = basename(scenarioPath);
const esmInstrumentBasename = basename(instrumentPath);
const esmScenarioPathForRun = join(tmpDirPath, esmScenarioBasename);
const esmInstrumentPathForRun = join(tmpDirPath, esmInstrumentBasename);
writeFileSync(esmScenarioPathForRun, readFileSync(mjsScenarioPath, 'utf8'));
writeFileSync(esmInstrumentPathForRun, readFileSync(mjsInstrumentPath, 'utf8'));

// Pre-create CJS converted files inside tmp dir
const cjsScenarioPath = join(tmpDirPath, esmScenarioBasename.replace('.mjs', '.cjs'));
const cjsInstrumentPath = join(tmpDirPath, esmInstrumentBasename.replace('.mjs', '.cjs'));
convertEsmFileToCjs(esmScenarioPathForRun, cjsScenarioPath);
convertEsmFileToCjs(esmInstrumentPathForRun, cjsInstrumentPath);

// Create a minimal package.json with requested dependencies (if any) and install them
const additionalDependencies = options?.additionalDependencies ?? {};
if (Object.keys(additionalDependencies).length > 0) {
const packageJson = {
name: 'tmp-integration-test',
private: true,
version: '0.0.0',
dependencies: additionalDependencies,
} as const;

writeFileSync(join(tmpDirPath, 'package.json'), JSON.stringify(packageJson, null, 2));

try {
const deps = Object.entries(additionalDependencies).map(([name, range]) => {
if (!range || typeof range !== 'string') {
throw new Error(`Invalid version range for "${name}": ${String(range)}`);
}
return `${name}@${range}`;
});

if (deps.length > 0) {
// Prefer npm for temp installs to avoid Yarn engine strictness; see https://github.com/vercel/ai/issues/7777
// We rely on the generated package.json dependencies and run a plain install.
const result = spawnSync('npm', ['install', '--silent', '--no-audit', '--no-fund'], {
cwd: tmpDirPath,
encoding: 'utf8',
function createTmpDir(): void {
mkdirSync(tmpDirPath);

// Copy ESM files as-is into tmp dir
writeFileSync(esmScenarioPathForRun, readFileSync(mjsScenarioPath, 'utf8'));
writeFileSync(esmInstrumentPathForRun, readFileSync(mjsInstrumentPath, 'utf8'));

// Pre-create CJS converted files inside tmp dir
convertEsmFileToCjs(esmScenarioPathForRun, cjsScenarioPath);
convertEsmFileToCjs(esmInstrumentPathForRun, cjsInstrumentPath);

// Create a minimal package.json with requested dependencies (if any) and install them
const additionalDependencies = options?.additionalDependencies ?? {};
if (Object.keys(additionalDependencies).length > 0) {
const packageJson = {
name: 'tmp-integration-test',
private: true,
version: '0.0.0',
dependencies: additionalDependencies,
} as const;

writeFileSync(join(tmpDirPath, 'package.json'), JSON.stringify(packageJson, null, 2));

try {
const deps = Object.entries(additionalDependencies).map(([name, range]) => {
if (!range || typeof range !== 'string') {
throw new Error(`Invalid version range for "${name}": ${String(range)}`);
}
return `${name}@${range}`;
});

if (process.env.DEBUG) {
// eslint-disable-next-line no-console
console.log('[additionalDependencies via npm]', deps.join(' '));
// eslint-disable-next-line no-console
console.log('[npm stdout]', result.stdout);
// eslint-disable-next-line no-console
console.log('[npm stderr]', result.stderr);
}
if (deps.length > 0) {
// Prefer npm for temp installs to avoid Yarn engine strictness; see https://github.com/vercel/ai/issues/7777
// We rely on the generated package.json dependencies and run a plain install.
const result = spawnSync('npm', ['install', '--silent', '--no-audit', '--no-fund'], {
cwd: tmpDirPath,
encoding: 'utf8',
});

if (result.error) {
throw new Error(`Failed to install additionalDependencies in tmp dir ${tmpDirPath}: ${result.error.message}`);
}
if (typeof result.status === 'number' && result.status !== 0) {
throw new Error(
`Failed to install additionalDependencies in tmp dir ${tmpDirPath} (exit ${result.status}):\n${
result.stderr || result.stdout || '(no output)'
}`,
);
if (process.env.DEBUG) {
// eslint-disable-next-line no-console
console.log('[additionalDependencies via npm]', deps.join(' '));
// eslint-disable-next-line no-console
console.log('[npm stdout]', result.stdout);
// eslint-disable-next-line no-console
console.log('[npm stderr]', result.stderr);
}

if (result.error) {
throw new Error(
`Failed to install additionalDependencies in tmp dir ${tmpDirPath}: ${result.error.message}`,
);
}
if (typeof result.status === 'number' && result.status !== 0) {
throw new Error(
`Failed to install additionalDependencies in tmp dir ${tmpDirPath} (exit ${result.status}):\n${
result.stderr || result.stdout || '(no output)'
}`,
);
}
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Failed to install additionalDependencies:', e);
throw e;
}
} catch (e) {
// eslint-disable-next-line no-console
console.error('Failed to install additionalDependencies:', e);
throw e;
}
}

Expand All @@ -281,6 +286,11 @@ export function createEsmAndCjsTests(
callback(() => createRunner(cjsScenarioPath).withFlags('--require', cjsInstrumentPath), cjsTestFn, 'cjs');
});

// Create tmp directory
beforeAll(() => {
createTmpDir();
});

// Clean up the tmp directory after both esm and cjs suites have run
afterAll(() => {
try {
Expand Down
Loading