import { fs } from '~test/util.ts'; import { GlobalConfig } from '../../../config/global.ts'; import type { InternalGlobalConfigOptions, RepoGlobalConfig, } from '../../../config/types.ts'; import { TEMPORARY_ERROR } from '../../../constants/error-messages.ts'; import { ExecError } from '../../../util/exec/exec-error.ts'; import { exec as _exec } from '../../../util/exec/index.ts'; import type { UpdateArtifact } from '../types.ts'; import { updateArtifacts } from './artifacts.ts'; vi.mock('../../../util/exec/index.ts'); vi.mock('../../../util/fs/index.ts'); const exec = vi.mocked(_exec); const globalConfig: RepoGlobalConfig & InternalGlobalConfigOptions = { localDir: '', binarySource: 'global', }; describe('modules/manager/bun/artifacts', () => { describe('updateArtifacts()', () => { let updateArtifact: UpdateArtifact; beforeEach(() => { GlobalConfig.set(globalConfig); updateArtifact = { config: {}, newPackageFileContent: '', packageFileName: '', updatedDeps: [], }; }); it('skips if no updatedDeps and no lockFileMaintenance', async () => { await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); it('skips if no lock file in config', async () => { updateArtifact.updatedDeps = [{}]; await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); describe('when using .lockb lockfile format', () => { it('skips if cannot read lock file', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); it('returns null if lock content unchanged', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); it('returns updated lock content', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lockb', type: 'addition', contents: newLock, }, }, ]); }); it('updates lock file when workspace package is updated', async () => { updateArtifact.packageFileName = 'packages/workspace-a/package.json'; updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); const result = await updateArtifacts(updateArtifact); expect(result).toEqual([ { file: { path: 'bun.lockb', type: 'addition', contents: newLock, }, }, ]); expect(exec).toHaveBeenCalledWith('bun install --ignore-scripts', { cwdFile: 'bun.lockb', docker: {}, toolConstraints: [ { toolName: 'bun', }, ], }); }); it('supports lockFileMaintenance', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; updateArtifact.config.isLockFileMaintenance = true; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lockb', type: 'addition', contents: newLock, }, }, ]); }); it('supports lockFileMaintenance (without updated deps)', async () => { updateArtifact.config.lockFiles = ['bun.lockb']; updateArtifact.config.isLockFileMaintenance = true; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lockb', type: 'addition', contents: newLock, }, }, ]); }); it('handles temporary error', async () => { const execError = new ExecError(TEMPORARY_ERROR, { cmd: '', stdout: '', stderr: '', options: {}, }); updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); exec.mockRejectedValueOnce(execError); await expect(updateArtifacts(updateArtifact)).rejects.toThrow( TEMPORARY_ERROR, ); }); it('handles full error', async () => { const execError = new ExecError('nope', { cmd: '', stdout: '', stderr: '', options: {}, }); updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lockb'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); exec.mockRejectedValueOnce(execError); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { artifactError: { fileName: 'bun.lockb', stderr: 'nope' } }, ]); }); }); describe('when using .lock lockfile format', () => { it('skips if cannot read lock file', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); it('returns null if lock content unchanged', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toBeNull(); }); it('returns updated lock content', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lock', type: 'addition', contents: newLock, }, }, ]); }); it('supports lockFileMaintenance', async () => { updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; updateArtifact.config.isLockFileMaintenance = true; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lock', type: 'addition', contents: newLock, }, }, ]); }); it('supports lockFileMaintenance (without updated deps)', async () => { updateArtifact.config.lockFiles = ['bun.lock']; updateArtifact.config.isLockFileMaintenance = true; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); // npmrc fs.readLocalFile.mockResolvedValueOnce('# dummy'); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { file: { path: 'bun.lock', type: 'addition', contents: newLock, }, }, ]); }); it('handles temporary error', async () => { const execError = new ExecError(TEMPORARY_ERROR, { cmd: '', stdout: '', stderr: '', options: {}, }); updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); exec.mockRejectedValueOnce(execError); await expect(updateArtifacts(updateArtifact)).rejects.toThrow( TEMPORARY_ERROR, ); }); it('handles full error', async () => { const execError = new ExecError('nope', { cmd: '', stdout: '', stderr: '', options: {}, }); updateArtifact.updatedDeps = [ { manager: 'bun', lockFiles: ['bun.lock'] }, ]; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); exec.mockRejectedValueOnce(execError); await expect(updateArtifacts(updateArtifact)).resolves.toEqual([ { artifactError: { fileName: 'bun.lock', stderr: 'nope' } }, ]); }); }); }); describe('bun command execution', () => { it('check install options with configs', async () => { const lockfileFormats = ['bun.lockb', 'bun.lock']; const testCases = [ { allowScripts: undefined, ignoreScripts: undefined, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: false, ignoreScripts: undefined, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: true, ignoreScripts: undefined, expectedCmd: 'bun install', }, { allowScripts: undefined, ignoreScripts: true, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: undefined, ignoreScripts: false, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: false, ignoreScripts: true, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: false, ignoreScripts: false, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: true, ignoreScripts: true, expectedCmd: 'bun install --ignore-scripts', }, { allowScripts: true, ignoreScripts: false, expectedCmd: 'bun install', }, ]; for (const lockFile of lockfileFormats) { for (const testCase of testCases) { GlobalConfig.set({ ...globalConfig, allowScripts: testCase.allowScripts, }); const updateArtifact: UpdateArtifact = { config: { ignoreScripts: testCase.ignoreScripts }, newPackageFileContent: '', packageFileName: '', updatedDeps: [{ manager: 'bun', lockFiles: [lockFile] }], }; const oldLock = Buffer.from('old'); fs.readLocalFile.mockResolvedValueOnce(oldLock as never); const newLock = Buffer.from('new'); fs.readLocalFile.mockResolvedValueOnce(newLock as never); await updateArtifacts(updateArtifact); expect(exec).toHaveBeenCalledExactlyOnceWith(testCase.expectedCmd, { cwdFile: lockFile, docker: {}, toolConstraints: [ { toolName: 'bun', }, ], }); exec.mockClear(); GlobalConfig.reset(); } } }); }); });