mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-31 17:51:49 +00:00
05e1271376
Enables the vitest/prefer-expect-resolves oxlint rule and applies its autofix across the spec suite.
355 lines
12 KiB
TypeScript
355 lines
12 KiB
TypeScript
import type { DirectoryResult } from 'tmp-promise';
|
|
import tmp from 'tmp-promise';
|
|
import { mockExecAll } from '~test/exec-util.ts';
|
|
import { fs } from '~test/util.ts';
|
|
import { GlobalConfig } from '../../../config/global.ts';
|
|
import { TEMPORARY_ERROR } from '../../../constants/error-messages.ts';
|
|
import { ExecError } from '../../../util/exec/exec-error.ts';
|
|
import * as hostRules from '../../../util/host-rules.ts';
|
|
import type { UpdateArtifact } from '../types.ts';
|
|
import { updateArtifacts } from './artifacts.ts';
|
|
|
|
vi.mock('../../../util/fs/index.ts');
|
|
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {
|
|
constraints: { deno: '2.4.5' },
|
|
},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [],
|
|
};
|
|
|
|
describe('modules/manager/deno/artifacts', () => {
|
|
beforeEach(() => {
|
|
hostRules.clear();
|
|
});
|
|
|
|
describe('updateArtifacts()', () => {
|
|
let localDirResult: DirectoryResult;
|
|
let localDir: string;
|
|
|
|
beforeEach(async () => {
|
|
localDirResult = await tmp.dir({ unsafeCleanup: true });
|
|
localDir = localDirResult.path;
|
|
|
|
GlobalConfig.set({ localDir, binarySource: 'global' });
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await localDirResult?.cleanup();
|
|
});
|
|
|
|
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();
|
|
});
|
|
|
|
it('skips and returns an error if cannot read lock file', async () => {
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'deno.lock',
|
|
stderr: `Failed to read "deno.lock"`,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('returns null if lock content unchanged', async () => {
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toBeNull();
|
|
});
|
|
|
|
it('returns updated lock content', async () => {
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
file: {
|
|
path: 'deno.lock',
|
|
type: 'addition',
|
|
contents: newLock,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('change directory if import map is used', async () => {
|
|
updateArtifact.updatedDeps = [
|
|
{
|
|
manager: 'deno',
|
|
lockFiles: ['sub/deno.lock'],
|
|
packageFile: 'import_map.json',
|
|
managerData: { importMapReferrer: 'sub/deno.json' },
|
|
},
|
|
];
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
file: {
|
|
path: 'sub/deno.lock',
|
|
type: 'addition',
|
|
contents: newLock,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('supports lockFileMaintenance', async () => {
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
updateArtifact.config.updateType = 'lockFileMaintenance';
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
file: {
|
|
path: 'deno.lock',
|
|
type: 'addition',
|
|
contents: newLock,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('handles temporary error', async () => {
|
|
const execError = new ExecError(TEMPORARY_ERROR, {
|
|
cmd: '',
|
|
stdout: '',
|
|
stderr: '',
|
|
options: { encoding: 'utf8' },
|
|
});
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
mockExecAll(execError);
|
|
await expect(updateArtifacts(updateArtifact)).rejects.toThrow(
|
|
TEMPORARY_ERROR,
|
|
);
|
|
});
|
|
|
|
it('handles full error', async () => {
|
|
const execError = new ExecError('nope', {
|
|
cmd: '',
|
|
stdout: '',
|
|
stderr: '',
|
|
options: { encoding: 'utf8' },
|
|
});
|
|
updateArtifact.updatedDeps = [{ lockFiles: ['deno.lock'] }];
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
mockExecAll(execError);
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{ artifactError: { fileName: 'deno.lock', stderr: 'nope' } },
|
|
]);
|
|
});
|
|
});
|
|
|
|
it('depType tasks returns an error', async () => {
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [
|
|
{ lockFiles: ['deno.lock'], depType: 'tasks', depName: 'dep1' },
|
|
],
|
|
};
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'deno.lock',
|
|
stderr: `depType: "tasks", depName: "dep1" can't be updated with a lock file: "deno.lock"`,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('depType tasks.command returns an error', async () => {
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [
|
|
{ lockFiles: ['deno.lock'], depType: 'tasks.command', depName: 'dep1' },
|
|
],
|
|
};
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
artifactError: {
|
|
fileName: 'deno.lock',
|
|
stderr: `depType: "tasks.command", depName: "dep1" can't be updated with a lock file: "deno.lock"`,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('supports lockFileMaintenance (without updated deps)', async () => {
|
|
updateArtifact.updatedDeps = [];
|
|
updateArtifact.config.lockFiles = ['deno.lock'];
|
|
updateArtifact.config.isLockFileMaintenance = true;
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
const execSnapshots = mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
file: {
|
|
path: 'deno.lock',
|
|
type: 'addition',
|
|
contents: newLock,
|
|
},
|
|
},
|
|
]);
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: 'deno install --frozen=false',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('deno command execution', async () => {
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [{ lockFiles: ['deno.lock'] }],
|
|
};
|
|
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
const execSnapshots = mockExecAll();
|
|
|
|
await updateArtifacts(updateArtifact);
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: 'deno install --frozen=false',
|
|
},
|
|
]);
|
|
});
|
|
|
|
describe('private registries', () => {
|
|
it('should add private registries to deno install command allow-import option', async () => {
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {
|
|
updateType: 'lockFileMaintenance',
|
|
lockFiles: ['deno.lock'],
|
|
},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [],
|
|
};
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
hostRules.add({
|
|
token: 'some-token',
|
|
hostType: 'npm',
|
|
matchHost: 'https://private-registry.example',
|
|
});
|
|
const execSnapshots = mockExecAll();
|
|
await expect(updateArtifacts(updateArtifact)).resolves.toEqual([
|
|
{
|
|
file: {
|
|
path: 'deno.lock',
|
|
type: 'addition',
|
|
contents: newLock,
|
|
},
|
|
},
|
|
]);
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: 'deno install --frozen=false --allow-import=deno.land:443,esm.sh:443,jsr.io:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,gist.githubusercontent.com:443,private-registry.example',
|
|
},
|
|
]);
|
|
});
|
|
|
|
it('quotes the allow-import list when a hostRule resolvedHost contains shell metacharacters', async () => {
|
|
const updateArtifact: UpdateArtifact = {
|
|
config: {
|
|
updateType: 'lockFileMaintenance',
|
|
lockFiles: ['deno.lock'],
|
|
},
|
|
newPackageFileContent: '',
|
|
packageFileName: '',
|
|
updatedDeps: [],
|
|
};
|
|
const oldLock = Buffer.from('old');
|
|
fs.readLocalFile.mockResolvedValueOnce(oldLock as never);
|
|
// Second read is .npmrc
|
|
fs.readLocalFile.mockResolvedValueOnce(null);
|
|
const newLock = Buffer.from('new');
|
|
fs.readLocalFile.mockResolvedValueOnce(newLock as never);
|
|
// hostRules can be set via a repo's own committed config, not only by a
|
|
// trusted platform admin, so matchHost/resolvedHost aren't inherently
|
|
// trusted. A bare (schemeless) matchHost is preserved verbatim as
|
|
// resolvedHost when it doesn't parse as a URL.
|
|
hostRules.add({
|
|
token: 'some-token',
|
|
hostType: 'npm',
|
|
matchHost: 'x$(touch pwned)y',
|
|
});
|
|
const execSnapshots = mockExecAll();
|
|
|
|
await updateArtifacts(updateArtifact);
|
|
|
|
expect(execSnapshots).toMatchObject([
|
|
{
|
|
cmd: "deno install --frozen=false --allow-import='deno.land:443,esm.sh:443,jsr.io:443,cdn.jsdelivr.net:443,raw.githubusercontent.com:443,gist.githubusercontent.com:443,x$(touch pwned)y'",
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
});
|