mirror of
https://github.com/renovatebot/renovate.git
synced 2026-11-01 10:09:50 +00:00
05e1271376
Enables the vitest/prefer-expect-resolves oxlint rule and applies its autofix across the spec suite.
533 lines
14 KiB
TypeScript
533 lines
14 KiB
TypeScript
import { Fixtures } from '~test/fixtures.ts';
|
|
import * as httpMock from '~test/http-mock.ts';
|
|
import { EXTERNAL_HOST_ERROR } from '../../../constants/error-messages.ts';
|
|
import * as hostRules from '../../../util/host-rules.ts';
|
|
import { id as versioning } from '../../versioning/loose/index.ts';
|
|
import { getPkgReleases } from '../index.ts';
|
|
import { RepologyDatasource } from './index.ts';
|
|
import type { RepologyPackage } from './schema.ts';
|
|
|
|
const datasource = RepologyDatasource.id;
|
|
|
|
const repologyHost = 'https://repology.org/';
|
|
|
|
interface ResponseMock {
|
|
status?: number;
|
|
body?: string;
|
|
code?: string;
|
|
}
|
|
|
|
function mockApiCall(name: string, response: ResponseMock) {
|
|
const interceptor = httpMock
|
|
.scope(repologyHost)
|
|
.get(`/api/v1/project/${name}`);
|
|
if (response.status) {
|
|
interceptor.reply(response.status, response.body);
|
|
} else {
|
|
interceptor.replyWithError(httpMock.error({ code: response.code }));
|
|
}
|
|
}
|
|
|
|
function mockResolverCall(
|
|
repo: string,
|
|
name: string,
|
|
name_type: string,
|
|
response: ResponseMock,
|
|
) {
|
|
const query = {
|
|
repo,
|
|
name_type,
|
|
target_page: 'api_v1_project',
|
|
noautoresolve: 'on',
|
|
name,
|
|
};
|
|
|
|
const interceptor = httpMock
|
|
.scope(repologyHost)
|
|
.get('/tools/project-by')
|
|
.query(query);
|
|
if (response.status) {
|
|
interceptor.reply(response.status, response.body);
|
|
} else {
|
|
interceptor.replyWithError(httpMock.error({ code: response.code }));
|
|
}
|
|
}
|
|
|
|
const fixtureNginx = Fixtures.get(`nginx.json`);
|
|
const fixtureGccDefaults = Fixtures.get(`gcc-defaults.json`);
|
|
const fixtureGcc = Fixtures.get(`gcc.json`);
|
|
const fixturePulseaudio = Fixtures.get(`pulseaudio.json`);
|
|
const fixtureJdk = Fixtures.get(`openjdk.json`);
|
|
const fixturePython = Fixtures.get(`python.json`);
|
|
|
|
describe('modules/datasource/repology/index', () => {
|
|
beforeEach(() => {
|
|
hostRules.clear();
|
|
});
|
|
|
|
describe('getReleases', () => {
|
|
it('returns null for empty result', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 200,
|
|
body: '[]',
|
|
});
|
|
mockResolverCall('debian_stable', 'nginx', 'srcname', {
|
|
status: 200,
|
|
body: '[]',
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('returns null for missing repository or package', async () => {
|
|
mockResolverCall('this_should', 'never-exist', 'binname', {
|
|
status: 404,
|
|
});
|
|
mockResolverCall('this_should', 'never-exist', 'srcname', {
|
|
status: 404,
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'this_should/never-exist',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('throws error on unexpected API response', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 200,
|
|
body: '[]',
|
|
});
|
|
mockResolverCall('debian_stable', 'nginx', 'srcname', {
|
|
status: 403,
|
|
});
|
|
mockApiCall('nginx', { status: 500 });
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
|
});
|
|
|
|
it('throws error on unexpected Resolver response with binary package', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 500,
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
|
});
|
|
|
|
it('throws error on unexpected Resolver response with source package', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 200,
|
|
body: '[]',
|
|
});
|
|
mockResolverCall('debian_stable', 'nginx', 'srcname', {
|
|
status: 500,
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
|
});
|
|
|
|
it('skips package instead of aborting the run on API request timeout', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 200,
|
|
body: '[]',
|
|
});
|
|
mockResolverCall('debian_stable', 'nginx', 'srcname', {
|
|
status: 403,
|
|
});
|
|
mockApiCall('nginx', { code: 'ETIMEDOUT' });
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('skips package instead of aborting the run on Resolver request timeout', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
code: 'ETIMEDOUT',
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('aborts the run on timeout when abortOnError is enabled for the host', async () => {
|
|
// With hostRules abortOnError: true, the HTTP layer wraps the timeout as
|
|
// an ExternalHostError, which handleGenericErrors re-throws, so the run
|
|
// still aborts. This preserves the documented opt-in behavior.
|
|
hostRules.add({ matchHost: 'repology.org', abortOnError: true });
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
code: 'ETIMEDOUT',
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
|
});
|
|
|
|
it('returns null on Resolver ambiguous binary package', async () => {
|
|
mockResolverCall('ubuntu_20_04', 'git', 'binname', {
|
|
status: 300,
|
|
body: '[]',
|
|
});
|
|
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'ubuntu_20_04/git',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('throws without repository and package name', async () => {
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'invalid-lookup-name',
|
|
}),
|
|
).rejects.toThrow(EXTERNAL_HOST_ERROR);
|
|
});
|
|
|
|
it('throws on disabled host', async () => {
|
|
hostRules.add({ matchHost: repologyHost, enabled: false });
|
|
await expect(
|
|
getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
}),
|
|
).resolves.toBeNull();
|
|
});
|
|
|
|
it('returns correct version for binary package', async () => {
|
|
mockResolverCall('debian_stable', 'nginx', 'binname', {
|
|
status: 200,
|
|
body: fixtureNginx,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/nginx',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '1.14.2-2+deb10u1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns correct version for source package', async () => {
|
|
mockResolverCall('debian_stable', 'gcc-defaults', 'binname', {
|
|
status: 404,
|
|
});
|
|
mockResolverCall('debian_stable', 'gcc-defaults', 'srcname', {
|
|
status: 200,
|
|
body: fixtureGccDefaults,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/gcc-defaults',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '1.181',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns correct version for api package', async () => {
|
|
mockResolverCall('debian_stable', 'gcc-defaults', 'binname', {
|
|
status: 403,
|
|
});
|
|
mockApiCall('gcc-defaults', { status: 200, body: fixtureGccDefaults });
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/gcc-defaults',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '1.181',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns correct version for multi-package project with same name', async () => {
|
|
mockResolverCall('alpine_3_12', 'gcc', 'binname', {
|
|
status: 200,
|
|
body: fixtureGcc,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'alpine_3_12/gcc',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '9.3.0-r2',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns correct version for multi-package project with different name', async () => {
|
|
mockResolverCall('debian_stable', 'pulseaudio-utils', 'binname', {
|
|
status: 200,
|
|
body: fixturePulseaudio,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'debian_stable/pulseaudio-utils',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '12.2-4+deb10u1',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns multiple versions if they are present in repository', async () => {
|
|
mockResolverCall('centos_8', 'java-11-openjdk', 'binname', {
|
|
status: 404,
|
|
});
|
|
mockResolverCall('centos_8', 'java-11-openjdk', 'srcname', {
|
|
status: 200,
|
|
body: fixtureJdk,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'centos_8/java-11-openjdk',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{
|
|
version: '1:11.0.7.10-1.el8_1',
|
|
},
|
|
{
|
|
version: '1:11.0.8.10-0.el8_2',
|
|
},
|
|
{
|
|
version: '1:11.0.8.10-6.el8',
|
|
},
|
|
{
|
|
version: '1:11.0.9.11-0.el8_2',
|
|
},
|
|
{
|
|
version: '1:11.0.9.11-2.el8_3',
|
|
},
|
|
{
|
|
version: '1:11.0.9.11-3.el8_3',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns null for scenario when repo is not in package results', async () => {
|
|
const pkgs: RepologyPackage[] = [
|
|
{ repo: 'not-dummy', version: '1.0.0', visiblename: 'example' },
|
|
{ repo: 'not-dummy', version: '2.0.0', visiblename: 'example' },
|
|
];
|
|
const pkgsJSON = JSON.stringify(pkgs);
|
|
|
|
mockResolverCall('dummy', 'example', 'binname', {
|
|
status: 200,
|
|
body: pkgsJSON,
|
|
});
|
|
|
|
mockResolverCall('dummy', 'example', 'srcname', {
|
|
status: 200,
|
|
body: pkgsJSON,
|
|
});
|
|
|
|
const release = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'dummy/example',
|
|
});
|
|
|
|
expect(release).toBeNull();
|
|
});
|
|
|
|
it('returns correct package types for api_call', async () => {
|
|
const pkgs: RepologyPackage[] = [
|
|
{
|
|
repo: 'some_repo',
|
|
version: '1.0.0',
|
|
visiblename: 'some-package',
|
|
srcname: 'some-package',
|
|
},
|
|
{
|
|
repo: 'some_repo',
|
|
version: '2.0.0',
|
|
visiblename: 'not-some-package',
|
|
srcname: 'not-some-package',
|
|
},
|
|
{
|
|
repo: 'some_repo',
|
|
version: '3.0.0',
|
|
visiblename: 'some-package',
|
|
srcname: 'not-some-package',
|
|
},
|
|
{
|
|
repo: 'some_repo',
|
|
version: '4.0.0',
|
|
visiblename: 'some-package',
|
|
binname: 'some-package',
|
|
},
|
|
{
|
|
repo: 'some_repo',
|
|
version: '5.0.0',
|
|
visiblename: 'not-some-package',
|
|
binname: 'not-some-package',
|
|
},
|
|
{
|
|
repo: 'some_repo',
|
|
version: '6.0.0',
|
|
visiblename: 'some-package',
|
|
binname: 'not-some-package',
|
|
},
|
|
{ repo: 'some_repo', version: '7.0.0', visiblename: 'some-package' },
|
|
{
|
|
repo: 'some_repo',
|
|
version: '8.0.0',
|
|
visiblename: 'not-some-package',
|
|
},
|
|
{
|
|
repo: 'not_some_repo',
|
|
version: '9.0.0',
|
|
visiblename: 'some-package',
|
|
},
|
|
{
|
|
repo: 'not_some_repo',
|
|
version: '10.0.0',
|
|
visiblename: 'some-package',
|
|
srcname: 'some-package',
|
|
},
|
|
{
|
|
repo: 'not_some_repo',
|
|
version: '11.0.0',
|
|
visiblename: 'some-package',
|
|
binname: 'some-package',
|
|
},
|
|
];
|
|
const pkgsJSON = JSON.stringify(pkgs);
|
|
|
|
mockResolverCall('some_repo', 'some-package', 'binname', {
|
|
status: 403,
|
|
});
|
|
|
|
mockApiCall('some-package', {
|
|
status: 200,
|
|
body: pkgsJSON,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'some_repo/some-package',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{ version: '1.0.0' },
|
|
{
|
|
version: '4.0.0',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it('returns correct package versions for multi-package project', async () => {
|
|
mockResolverCall('ubuntu_20_04', 'python3.8', 'binname', {
|
|
status: 200,
|
|
body: fixturePython,
|
|
});
|
|
|
|
mockResolverCall('ubuntu_20_04', 'python3.8', 'srcname', {
|
|
status: 200,
|
|
body: fixturePython,
|
|
});
|
|
|
|
const res = await getPkgReleases({
|
|
datasource,
|
|
versioning,
|
|
packageName: 'ubuntu_20_04/python3.8',
|
|
});
|
|
expect(res).toEqual({
|
|
registryUrl: 'https://repology.org',
|
|
releases: [
|
|
{ version: '3.8.2-1ubuntu1' },
|
|
{
|
|
version: '3.8.10-0ubuntu1~20.04.2',
|
|
},
|
|
],
|
|
});
|
|
});
|
|
});
|
|
});
|