Files
ccs/tests/integration/update-command-install-origin.test.ts

186 lines
5.3 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
let tempRoot = '';
let currentPackageRoot = '';
let currentPrefix = '';
let bunPackageRoot = '';
let fakeBinDir = '';
let originalArgv1 = '';
let originalPath = '';
let originalConsoleLog: typeof console.log;
let originalProcessExit: typeof process.exit;
let logLines: string[] = [];
let exitCodes: number[] = [];
function writePackage(root: string, version: string): void {
mkdirSync(root, { recursive: true });
writeFileSync(
join(root, 'package.json'),
JSON.stringify({ name: '@kaitranntt/ccs', version }, null, 2)
);
}
function readPackageVersion(root: string): string {
return JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).version as string;
}
function writeExecutable(filePath: string, contents: string): void {
writeFileSync(filePath, contents);
chmodSync(filePath, 0o755);
}
async function loadHandleUpdateCommand() {
const mod = await import(`../../src/commands/update-command?test=${Date.now()}-${Math.random()}`);
return mod.handleUpdateCommand;
}
beforeEach(() => {
tempRoot = mkdtempSync(join(tmpdir(), 'ccs-update-origin-'));
currentPrefix = join(tempRoot, 'prefix');
currentPackageRoot = join(currentPrefix, 'lib', 'node_modules', '@kaitranntt', 'ccs');
bunPackageRoot = join(
tempRoot,
'.bun',
'install',
'global',
'node_modules',
'@kaitranntt',
'ccs'
);
fakeBinDir = join(tempRoot, 'bin');
mkdirSync(join(currentPackageRoot, 'dist'), { recursive: true });
mkdirSync(fakeBinDir, { recursive: true });
writePackage(currentPackageRoot, '7.67.0-dev.5');
writePackage(bunPackageRoot, '0.0.0-stale');
originalArgv1 = process.argv[1] ?? '';
process.argv[1] = join(currentPackageRoot, 'dist', 'ccs.js');
originalPath = process.env.PATH ?? '';
process.env.PATH = `${fakeBinDir}:${originalPath}`;
logLines = [];
exitCodes = [];
originalConsoleLog = console.log;
console.log = (...args: unknown[]) => {
logLines.push(args.map(String).join(' '));
};
originalProcessExit = process.exit;
process.exit = ((code?: number) => {
exitCodes.push(code ?? 0);
}) as typeof process.exit;
});
afterEach(() => {
console.log = originalConsoleLog;
process.exit = originalProcessExit;
process.argv[1] = originalArgv1;
process.env.PATH = originalPath;
if (tempRoot) {
rmSync(tempRoot, { recursive: true, force: true });
}
});
async function waitForExitCode(expectedCode: number): Promise<void> {
for (let attempt = 0; attempt < 50; attempt++) {
if (exitCodes.includes(expectedCode)) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 10));
}
throw new Error(`process.exit(${expectedCode}) was not observed`);
}
describe('update-command install origin integration', () => {
it('updates the current npm-owned install instead of drifting to another manager', async () => {
writeExecutable(
join(fakeBinDir, 'npm'),
`#!/bin/sh
if [ "$npm_config_prefix" = "${currentPrefix}" ] || [ "$NPM_CONFIG_PREFIX" = "${currentPrefix}" ]; then
cat > "${join(currentPackageRoot, 'package.json')}" <<'EOF'
{"name":"@kaitranntt/ccs","version":"7.67.0-dev.9"}
EOF
exit 0
fi
exit 13
`
);
writeExecutable(
join(fakeBinDir, 'bun'),
`#!/bin/sh
cat > "${join(bunPackageRoot, 'package.json')}" <<'EOF'
{"name":"@kaitranntt/ccs","version":"7.67.0-dev.9"}
EOF
exit 0
`
);
const handleUpdateCommand = await loadHandleUpdateCommand();
await handleUpdateCommand(
{ beta: true },
{
initUI: async () => {},
getVersion: () => '7.67.0-dev.5',
compareVersionsWithPrerelease: (left: string, right: string) => left.localeCompare(right),
checkForUpdates: async () => ({
status: 'update_available',
current: '7.67.0-dev.5',
latest: '7.67.0-dev.9',
}),
}
);
await waitForExitCode(0);
expect(readPackageVersion(currentPackageRoot)).toBe('7.67.0-dev.9');
expect(readPackageVersion(bunPackageRoot)).toBe('0.0.0-stale');
expect(logLines.join('\n')).toContain('Updating via npm');
});
it('fails if the update exits 0 but the current install stays stale', async () => {
writeExecutable(
join(fakeBinDir, 'npm'),
`#!/bin/sh
exit 0
`
);
writeExecutable(
join(fakeBinDir, 'bun'),
`#!/bin/sh
cat > "${join(bunPackageRoot, 'package.json')}" <<'EOF'
{"name":"@kaitranntt/ccs","version":"7.67.0-dev.9"}
EOF
exit 0
`
);
const handleUpdateCommand = await loadHandleUpdateCommand();
await handleUpdateCommand(
{ beta: true },
{
initUI: async () => {},
getVersion: () => '7.67.0-dev.5',
compareVersionsWithPrerelease: (left: string, right: string) => left.localeCompare(right),
checkForUpdates: async () => ({
status: 'update_available',
current: '7.67.0-dev.5',
latest: '7.67.0-dev.9',
}),
}
);
await waitForExitCode(1);
expect(readPackageVersion(currentPackageRoot)).toBe('7.67.0-dev.5');
expect(logLines.join('\n')).toContain('outside the current installation');
expect(logLines.join('\n')).toContain('NPM_CONFIG_PREFIX=');
});
});