Files
renovate/lib/modules/manager/nuget/artifacts.spec.ts
Sebastian Poxhofer 05e1271376 test: use expect().resolves instead of awaiting inside expect (#45672)
Enables the vitest/prefer-expect-resolves oxlint rule and applies its
autofix across the spec suite.
2026-09-04 10:40:46 +00:00

520 lines
16 KiB
TypeScript

import upath from 'upath';
import { envMock, mockExecAll } from '~test/exec-util.ts';
import { env, fs, git, scm } 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 * as docker from '../../../util/exec/docker/index.ts';
import { nugetOrg } from '../../datasource/nuget/index.ts';
import type { UpdateArtifactsConfig } from '../types.ts';
import * as nuget from './index.ts';
import * as util from './util.ts';
vi.mock('../../../util/exec/env.ts');
vi.mock('../../../util/fs/index.ts');
vi.mock('./util.ts');
const { getDefaultRegistries, findGlobalJson } = vi.mocked(util);
process.env.CONTAINERBASE = 'true';
const adminConfig: RepoGlobalConfig & InternalGlobalConfigOptions = {
// `join` fixes Windows CI
localDir: upath.join('/tmp/github/some/repo'),
cacheDir: upath.join('/tmp/renovate/cache'),
containerbaseDir: upath.join('/tmp/renovate/cache/containerbase'),
binarySource: 'global',
};
const config: UpdateArtifactsConfig = {};
describe('modules/manager/nuget/artifacts', () => {
beforeEach(async () => {
const realFs = await vi.importActual<
typeof import('../../../util/fs/index.ts')
>('../../../util/fs/index.ts');
getDefaultRegistries.mockReturnValue([
{ url: nugetOrg, name: 'nuget.org' },
]);
env.getChildProcessEnv.mockReturnValue(envMock.basic);
fs.privateCacheDir.mockImplementation(realFs.privateCacheDir);
scm.getFileList.mockResolvedValueOnce([]);
GlobalConfig.set(adminConfig);
docker.resetPrefetchedImages();
});
afterEach(() => {
GlobalConfig.reset();
});
it('re-throws TEMPORARY_ERROR', async () => {
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
fs.writeLocalFile.mockRejectedValueOnce(new Error(TEMPORARY_ERROR));
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).rejects.toThrow(TEMPORARY_ERROR);
});
it('aborts if no lock file found', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({ 'packages.lock.json': null });
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toBeNull();
expect(execSnapshots).toBeEmptyArray();
});
it('aborts if lock file is unchanged', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce(
'path/with space/packages.lock.json',
);
git.getFiles.mockResolvedValueOnce({
'path/with space/packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'path/with space/packages.lock.json': 'Current packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'path/with space/project.csproj',
updatedDeps: [{ depName: 'foo' }, { depName: 'bar' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toBeNull();
expect(execSnapshots).toMatchObject([
{
cmd: "dotnet restore 'path/with space/project.csproj' --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config",
options: {
cwd: '/tmp/github/some/repo',
env: {
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('runs workload restore and updates lock file', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [
{ depName: 'dep', registryUrls: ['https://contoso.com/packages/'] },
],
newPackageFileContent: '{}',
config: { ...config, postUpdateOptions: ['dotnetWorkloadRestore'] },
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'dotnet workload restore --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
options: {
cwd: '/tmp/github/some/repo',
env: {
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
{
cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
options: {
cwd: '/tmp/github/some/repo',
env: {
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('does not update lock file when non-proj file is changed', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'otherfile.props',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toBeNull();
expect(execSnapshots).toBeEmptyArray();
});
it('updates lock file for Directory.Build.props', async () => {
const execSnapshots = mockExecAll();
scm.getFileList.mockReset();
scm.getFileList.mockResolvedValueOnce(['project.csproj']);
fs.getSiblingFileName
.mockReturnValueOnce('packages.lock.json')
.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'Directory.Build.props',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
},
]);
});
it('updates lock file for nested Directory.Build.props', async () => {
const execSnapshots = mockExecAll();
scm.getFileList.mockReset();
scm.getFileList.mockResolvedValueOnce(['src/project.csproj']);
fs.getSiblingFileName
.mockReturnValueOnce('src/packages.lock.json')
.mockReturnValueOnce('src/packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'src/packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'src/packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'src/Directory.Build.props',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'src/packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'dotnet restore src/project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
},
]);
});
it('does not update lock file when no deps changed', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [],
newPackageFileContent: '{}',
config,
}),
).resolves.toBeNull();
expect(execSnapshots).toBeEmptyArray();
});
it('performs lock file maintenance', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [],
newPackageFileContent: '{}',
config: {
...config,
isLockFileMaintenance: true,
},
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
options: {
cwd: '/tmp/github/some/repo',
env: {
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('supports docker mode', async () => {
GlobalConfig.set({
...adminConfig,
binarySource: 'docker',
dockerSidecarImage: 'ghcr.io/renovatebot/base-image',
});
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
findGlobalJson.mockResolvedValueOnce({ sdk: { version: '7.0.100' } });
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'docker pull ghcr.io/renovatebot/base-image',
},
{
cmd: 'docker ps --filter name=renovate_sidecar -aq',
},
{
cmd:
'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
'-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
'-v "/tmp/renovate/cache":"/tmp/renovate/cache" ' +
'-e CI ' +
'-e NUGET_PACKAGES ' +
'-e MSBUILDDISABLENODEREUSE ' +
'-e CONTAINERBASE_CACHE_DIR ' +
'-w "/tmp/github/some/repo" ' +
'ghcr.io/renovatebot/base-image ' +
"bash -l -c '" +
'install-tool dotnet 7.0.100' +
' && ' +
'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config' +
"'",
options: {
env: {
CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase',
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('supports install mode', async () => {
GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config: { ...config, constraints: { dotnet: '7.0.100' } },
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'install-tool dotnet 7.0.100',
options: {
cwd: '/tmp/github/some/repo',
env: {
CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase',
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
{
cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
options: {
cwd: '/tmp/github/some/repo',
env: {
CONTAINERBASE_CACHE_DIR: '/tmp/renovate/cache/containerbase',
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('supports global mode', async () => {
GlobalConfig.set({ ...adminConfig, binarySource: 'global' });
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.getLocalFiles.mockResolvedValueOnce({
'packages.lock.json': 'New packages.lock.json',
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toEqual([
{
file: {
contents: 'New packages.lock.json',
path: 'packages.lock.json',
type: 'addition',
},
},
]);
expect(execSnapshots).toMatchObject([
{
cmd: 'dotnet restore project.csproj --force-evaluate --configfile /tmp/renovate/cache/__renovate-private-cache/nuget/nuget.config',
options: {
cwd: '/tmp/github/some/repo',
env: {
NUGET_PACKAGES:
'/tmp/renovate/cache/__renovate-private-cache/nuget/packages',
MSBUILDDISABLENODEREUSE: '1',
},
},
},
]);
});
it('catches errors', async () => {
const execSnapshots = mockExecAll();
fs.getSiblingFileName.mockReturnValueOnce('packages.lock.json');
git.getFiles.mockResolvedValueOnce({
'packages.lock.json': 'Current packages.lock.json',
});
fs.writeLocalFile.mockImplementationOnce(() => {
throw new Error('not found');
});
await expect(
nuget.updateArtifacts({
packageFileName: 'project.csproj',
updatedDeps: [{ depName: 'dep' }],
newPackageFileContent: '{}',
config,
}),
).resolves.toEqual([
{
artifactError: {
fileName: 'packages.lock.json',
stderr: 'not found',
},
},
]);
expect(execSnapshots).toBeEmptyArray();
});
});