import upath from 'upath'; import { envMock, mockExecAll } from '~test/exec-util.ts'; import { env, 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 { logger } from '../../../logger/index.ts'; import type { UpdateArtifactsConfig } from '../types.ts'; import * as conan from './index.ts'; vi.mock('../../../util/exec/env.ts'); vi.mock('../../../util/fs/index.ts'); process.env.CONTAINERBASE = 'true'; const config: UpdateArtifactsConfig = {}; const adminConfig: RepoGlobalConfig & InternalGlobalConfigOptions = { localDir: upath.join('/tmp/github/some/repo'), cacheDir: upath.join('/tmp/cache'), containerbaseDir: upath.join('/tmp/cache/containerbase'), dockerSidecarImage: 'ghcr.io/renovatebot/base-image', binarySource: 'global', }; describe('modules/manager/conan/artifacts', () => { beforeEach(() => { env.getChildProcessEnv.mockReturnValue(envMock.basic); GlobalConfig.set(adminConfig); }); it('returns null if updatedDeps are empty and lockFileMaintenance is turned off', async () => { await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps: [], newPackageFileContent: '', config, }), ).resolves.toBeNull(); expect(logger.trace).toHaveBeenCalledWith( 'No conan.lock dependencies to update', ); }); it('returns null if conan.lock was not found', async () => { const updatedDeps = [ { depName: 'dep', }, ]; await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toBeNull(); expect(logger.trace).toHaveBeenCalledWith('No conan.lock found'); }); it('returns null if conan.lock read operation failed', async () => { const updatedDeps = [ { depName: 'dep', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toBeNull(); expect(logger.debug).toHaveBeenCalledWith( 'conan.lock read operation failed', ); }); it('returns null if read operation failed for new conan.lock', async () => { const updatedDeps = [ { depName: 'dep', }, ]; const expectedInSnapshot = [ { cmd: 'conan lock create conanfile.py', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toBeNull(); expect(execSnapshots).toMatchObject(expectedInSnapshot); expect(logger.debug).toHaveBeenCalledWith( 'New conan.lock read operation failed', ); }); it('returns null if original and updated conan.lock files are the same', async () => { const updatedDeps = [ { depName: 'dep', }, ]; const expectedInSnapshot = [ { cmd: 'conan lock create conanfile.py', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toBeNull(); expect(execSnapshots).toMatchObject(expectedInSnapshot); expect(logger.trace).toHaveBeenCalledWith('conan.lock is unchanged'); }); it('returns updated conan.lock for conanfile.txt', async () => { const updatedDeps = [ { depName: 'dep', }, ]; const expectedInSnapshot = [ { cmd: 'conan lock create conanfile.txt', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); fs.readLocalFile.mockResolvedValueOnce('Updated conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.txt', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toEqual([ { file: { contents: 'Updated conan.lock', path: 'conan.lock', type: 'addition', }, }, ]); expect(execSnapshots).toMatchObject(expectedInSnapshot); }); it('supports install mode', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'install' }); const updatedDeps = [ { depName: 'dep', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); fs.readLocalFile.mockResolvedValueOnce('Updated conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config: { ...config, constraints: { conan: '2.0.5', python: '3.11.9' }, }, }), ).resolves.toEqual([ { file: { contents: 'Updated conan.lock', path: 'conan.lock', type: 'addition', }, }, ]); expect(execSnapshots).toMatchObject([ { cmd: 'install-tool python 3.11.9', }, { cmd: 'install-tool conan 2.0.5', }, { cmd: 'conan lock create conanfile.py', options: { cwd: '/tmp/github/some/repo', }, }, ]); }); it('returns updated conan.lock when updateType are not empty', async () => { const updatedDeps = [ { depName: 'dep', }, ]; const expectedInSnapshot = [ { cmd: 'conan lock create conanfile.py', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); fs.readLocalFile.mockResolvedValueOnce('Updated conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config, }), ).resolves.toEqual([ { file: { contents: 'Updated conan.lock', path: 'conan.lock', type: 'addition', }, }, ]); expect(execSnapshots).toMatchObject(expectedInSnapshot); }); it('returns updated conan.lock when updateType are empty, but isLockFileMaintenance is true', async () => { const expectedInSnapshot = [ { cmd: 'conan lock create conanfile.py --lockfile=""', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); const execSnapshots = mockExecAll(); fs.readLocalFile.mockResolvedValueOnce('Updated conan.lock'); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps: [], newPackageFileContent: '', config: { ...config, isLockFileMaintenance: true }, }), ).resolves.toEqual([ { file: { contents: 'Updated conan.lock', path: 'conan.lock', type: 'addition', }, }, ]); expect(execSnapshots).toMatchObject(expectedInSnapshot); }); it('rethrows temporary error', async () => { const updatedDeps = [ { depName: 'dep', }, ]; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); mockExecAll(new Error(TEMPORARY_ERROR)); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config: { ...config, updateType: 'lockFileMaintenance' }, }), ).rejects.toThrow(TEMPORARY_ERROR); }); it('returns an artifact error when conan.lock update fails', async () => { const updatedDeps = [ { depName: 'dep', }, ]; const errorMessage = 'conan.lock update execution failure message'; fs.findLocalSiblingOrParent.mockResolvedValueOnce('conan.lock'); fs.readLocalFile.mockResolvedValueOnce('Original conan.lock'); mockExecAll(new Error(errorMessage)); await expect( conan.updateArtifacts({ packageFileName: 'conanfile.py', updatedDeps, newPackageFileContent: '', config: { ...config, updateType: 'lockFileMaintenance' }, }), ).resolves.toEqual([ { artifactError: { fileName: 'conan.lock', stderr: errorMessage } }, ]); }); });