Files
renovate/lib/modules/datasource/postprocess-release.spec.ts
Tiago Novo 7f71c26283 fix(datasource): use release-level registryUrl in postprocessRelease (#45096)
When a datasource uses the 'merge' registryStrategy (e.g. maven),
mergeRegistries() tags each release with the registryUrl of the repo
that actually reported it. postprocessRelease() ignored this and used
the first configured/default registryUrl instead, causing the Maven
datasource to probe the wrong repository for release verification.
If the artifact didn't exist in that first repo, the release was
wrongly rejected and no update was proposed, even though a valid
release existed in another configured repository.
2026-09-04 11:23:50 +00:00

186 lines
5.4 KiB
TypeScript

import type { Timestamp } from '../../util/timestamp.ts';
import * as _datasourceCommon from './common.ts';
import { Datasource } from './datasource.ts';
import { postprocessRelease } from './postprocess-release.ts';
import type {
GetReleasesConfig,
PostprocessReleaseConfig,
PostprocessReleaseResult,
Release,
ReleaseResult,
} from './types.ts';
vi.mock('./common.ts');
const { getDatasourceFor } = vi.mocked(_datasourceCommon);
class DummyDatasource extends Datasource {
constructor() {
super('some-datasource');
}
override getReleases(_: GetReleasesConfig): Promise<ReleaseResult | null> {
return Promise.resolve(null);
}
}
describe('modules/datasource/postprocess-release', () => {
it('returns original release for empty datasource field', async () => {
const releaseOrig: Release = { version: '1.2.3' };
const release = await postprocessRelease(
{ packageName: 'some-package' },
releaseOrig,
);
expect(release).toBe(releaseOrig);
});
it('returns original release for missing datasource', async () => {
const releaseOrig: Release = { version: '1.2.3' };
getDatasourceFor.mockReturnValueOnce(null);
const release = await postprocessRelease(
{ datasource: 'some-datasource', packageName: 'some-package' },
releaseOrig,
);
expect(release).toBe(releaseOrig);
});
it('returns original release for datasource with missing `postprocessRelease` method', async () => {
const releaseOrig: Release = { version: '1.2.3' };
getDatasourceFor.mockReturnValueOnce(new DummyDatasource());
const release = await postprocessRelease(
{ datasource: 'some-datasource', packageName: 'some-package' },
releaseOrig,
);
expect(release).toBe(releaseOrig);
});
it('returns original release for datasource with missing `packageName` field', async () => {
class SomeDatasource extends DummyDatasource {
override postprocessRelease(
_config: PostprocessReleaseConfig,
release: Release,
): Promise<PostprocessReleaseResult> {
return Promise.resolve(release);
}
}
const releaseOrig: Release = { version: '1.2.3' };
getDatasourceFor.mockReturnValueOnce(new SomeDatasource());
const release = await postprocessRelease(
{ datasource: 'some-datasource' },
releaseOrig,
);
expect(release).toBe(releaseOrig);
});
it('updates release via `postprocessRelease` method', async () => {
const releaseOrig: Release = { version: '1.2.3' };
class SomeDatasource extends DummyDatasource {
override postprocessRelease(
_config: PostprocessReleaseConfig,
release: Release,
): Promise<PostprocessReleaseResult> {
release.releaseTimestamp = '2024-09-05' as Timestamp;
return Promise.resolve(release);
}
}
getDatasourceFor.mockReturnValueOnce(new SomeDatasource());
const release = await postprocessRelease(
{
datasource: 'some-datasource',
packageName: 'some-package',
registryUrl: 'https://example.com',
},
releaseOrig,
);
expect(release).toEqual({
version: '1.2.3',
releaseTimestamp: '2024-09-05',
});
});
it('rejects release via `postprocessRelease` method', async () => {
const releaseOrig: Release = { version: '1.2.3' };
class SomeDatasource extends DummyDatasource {
override postprocessRelease(
_config: PostprocessReleaseConfig,
_release: Release,
): Promise<PostprocessReleaseResult> {
return Promise.resolve('reject');
}
}
getDatasourceFor.mockReturnValueOnce(new SomeDatasource());
const release = await postprocessRelease(
{ datasource: 'some-datasource', packageName: 'some-package' },
releaseOrig,
);
expect(release).toBeNull();
});
it('uses release-level `registryUrl` over the config-level one', async () => {
const releaseOrig: Release = {
version: '1.2.3',
registryUrl: 'https://release-registry.example.com',
};
let usedRegistryUrl: string | null = null;
class SomeDatasource extends DummyDatasource {
override postprocessRelease(
config: PostprocessReleaseConfig,
release: Release,
): Promise<PostprocessReleaseResult> {
usedRegistryUrl = config.registryUrl;
return Promise.resolve(release);
}
}
getDatasourceFor.mockReturnValueOnce(new SomeDatasource());
await postprocessRelease(
{
datasource: 'some-datasource',
packageName: 'some-package',
registryUrl: 'https://config-registry.example.com',
},
releaseOrig,
);
expect(usedRegistryUrl).toBe('https://release-registry.example.com');
});
it('falls back when error was thrown', async () => {
const releaseOrig: Release = { version: '1.2.3' };
class SomeDatasource extends DummyDatasource {
override postprocessRelease(
_config: PostprocessReleaseConfig,
_release: Release,
): Promise<PostprocessReleaseResult> {
return Promise.reject(new Error('unknown error'));
}
}
getDatasourceFor.mockReturnValueOnce(new SomeDatasource());
const release = await postprocessRelease(
{
datasource: 'some-datasource',
packageName: 'some-package',
registryUrls: ['https://example.com'],
},
releaseOrig,
);
expect(release).toBe(releaseOrig);
});
});