mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-24 14:34:21 +00:00
8b3a066756
As we're now managing them via a `afterEach` in `test/setup.ts`, we can remove any entries in tests themselves. We don't update overrides of an outer hook or cases where there are more explicit `clear()`s. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
310 lines
10 KiB
TypeScript
310 lines
10 KiB
TypeScript
import { codeBlock } from 'common-tags';
|
|
import { logger } from '~test/util.ts';
|
|
import { GlobalConfig } from '../config/global.ts';
|
|
import { InheritConfig } from '../config/inherit.ts';
|
|
import { PLATFORM_FAMILIES } from '../constants/index.ts';
|
|
import { detectPlatform, getInheritedOrGlobal, parseJson } from './common.ts';
|
|
import * as hostRules from './host-rules.ts';
|
|
|
|
const validJsonString = `
|
|
{
|
|
"name": "John Doe",
|
|
"age": 30,
|
|
"city": "New York"
|
|
}
|
|
`;
|
|
const invalidJsonString = `
|
|
{
|
|
"name": "Alice",
|
|
"age": 25,
|
|
"city": "Los Angeles",
|
|
"hobbies": ["Reading", "Running", "Cooking"]
|
|
"isStudent": true
|
|
}
|
|
`;
|
|
const onlyJson5parsableString = `
|
|
{
|
|
name: "Bob",
|
|
age: 35,
|
|
city: 'San Francisco',
|
|
// This is a comment
|
|
"isMarried": false,
|
|
}
|
|
`;
|
|
const validJsoncString = `
|
|
{
|
|
// This is a comment
|
|
"name": "John Doe",
|
|
"age": 30,
|
|
"city": "New York"
|
|
}
|
|
`;
|
|
|
|
describe('util/common', () => {
|
|
describe('detectPlatform', () => {
|
|
it.each`
|
|
url | hostType
|
|
${'some-invalid@url:::'} | ${null}
|
|
${'https://enterprise.example.com/chalk/chalk'} | ${null}
|
|
${'https://dev.azure.com/my-organization/my-project/_git/my-repo.git'} | ${'azure'}
|
|
${'https://myorg.visualstudio.com/my-project/_git/my-repo.git'} | ${'azure'}
|
|
${'https://bitbucket.org/some-org/some-repo'} | ${'bitbucket'}
|
|
${'https://bitbucket.com/some-org/some-repo'} | ${'bitbucket'}
|
|
${'https://bitbucket.example.com/some-org/some-repo'} | ${'bitbucket-server'}
|
|
${'https://gitea.com/semantic-release/gitlab'} | ${'gitea'}
|
|
${'https://gitea.example.com/semantic-release/gitlab'} | ${'gitea'}
|
|
${'https://forgejo.example.com/semantic-release/gitlab'} | ${'forgejo'}
|
|
${'https://codeberg.org/forgejo/forgejo'} | ${'forgejo'}
|
|
${'https://codefloe.com/some-org/some-repo'} | ${'forgejo'}
|
|
${'https://github.com/semantic-release/gitlab'} | ${'github'}
|
|
${'https://github-enterprise.example.com/chalk/chalk'} | ${'github'}
|
|
${'https://gitlab.com/chalk/chalk'} | ${'gitlab'}
|
|
${'https://gitlab-enterprise.example.com/chalk/chalk'} | ${'gitlab'}
|
|
`('("$url") === $hostType', ({ url, hostType }) => {
|
|
expect(detectPlatform(url)).toBe(hostType);
|
|
});
|
|
|
|
it.each(
|
|
Object.entries(PLATFORM_FAMILIES).flatMap(([family, { knownHosts }]) =>
|
|
knownHosts.map((host) => ({ family, host })),
|
|
),
|
|
)('knows $host as $family', ({ family, host }) => {
|
|
expect(detectPlatform(`https://${host}/some-org/some-repo`)).toBe(family);
|
|
});
|
|
|
|
it('uses host rules', () => {
|
|
hostRules.add({
|
|
hostType: 'azure',
|
|
matchHost: 'az.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'bitbucket',
|
|
matchHost: 'bb.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'gitea',
|
|
matchHost: 'gt.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'forgejo',
|
|
matchHost: 'fj.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'github-changelog',
|
|
matchHost: 'gh.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'gitlab-changelog',
|
|
matchHost: 'gl.example.com',
|
|
});
|
|
hostRules.add({
|
|
hostType: 'unknown',
|
|
matchHost: 'f.example.com',
|
|
});
|
|
expect(detectPlatform('https://az.example.com/chalk/chalk')).toBe(
|
|
'azure',
|
|
);
|
|
expect(detectPlatform('https://bb.example.com/chalk/chalk')).toBe(
|
|
'bitbucket',
|
|
);
|
|
expect(detectPlatform('https://gt.example.com/chalk/chalk')).toBe(
|
|
'gitea',
|
|
);
|
|
expect(detectPlatform('https://fj.example.com/chalk/chalk')).toBe(
|
|
'forgejo',
|
|
);
|
|
expect(detectPlatform('https://gh.example.com/chalk/chalk')).toBe(
|
|
'github',
|
|
);
|
|
expect(detectPlatform('https://gl.example.com/chalk/chalk')).toBe(
|
|
'gitlab',
|
|
);
|
|
expect(detectPlatform('https://f.example.com/chalk/chalk')).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('parseJson', () => {
|
|
it('returns null', () => {
|
|
expect(parseJson(null, 'renovate.json')).toBeNull();
|
|
});
|
|
|
|
it('returns parsed json', () => {
|
|
expect(parseJson(validJsonString, 'renovate.json')).toEqual({
|
|
name: 'John Doe',
|
|
age: 30,
|
|
city: 'New York',
|
|
});
|
|
});
|
|
|
|
it('supports jsonc', () => {
|
|
const jsoncString = codeBlock`
|
|
{
|
|
// This is a comment (valid in JSONC, invalid in JSON5 when outside object properties)
|
|
"name": "Alice", // inline comment is valid in JSONC but not in JSON5
|
|
"age": 25, // JSON5 supports trailing commas, so this line is okay in both
|
|
"city": "Atlanta",
|
|
// JSONC allows comments here too
|
|
}
|
|
`;
|
|
|
|
expect(parseJson(jsoncString, 'renovate.json')).toEqual({
|
|
name: 'Alice',
|
|
age: 25,
|
|
city: 'Atlanta',
|
|
});
|
|
});
|
|
|
|
it('throws error for invalid json', () => {
|
|
expect(() => parseJson(invalidJsonString, 'renovate.json')).toThrow(
|
|
"JSON5: invalid character '\\\"' at 7:3",
|
|
);
|
|
});
|
|
|
|
it('catches and warns if content parsing failed with JSONC.parse but not with JSON5.parse', () => {
|
|
expect(parseJson(onlyJson5parsableString, 'renovate.json')).toEqual({
|
|
name: 'Bob',
|
|
age: 35,
|
|
city: 'San Francisco',
|
|
isMarried: false,
|
|
});
|
|
|
|
expect(logger.logger.warn).toHaveBeenCalledWith(
|
|
{ context: 'renovate.json' },
|
|
'File contents are invalid JSONC but parse using JSON5. Support for this will be removed in a future release so please change to a support .json5 file name or ensure correct JSON syntax.',
|
|
);
|
|
});
|
|
|
|
it('does not warn if filename ends with .jsonc', () => {
|
|
parseJson(validJsoncString, 'renovate.jsonc');
|
|
expect(logger.logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not warn if filename ends with .json5', () => {
|
|
parseJson(onlyJson5parsableString, 'renovate.json5');
|
|
expect(logger.logger.warn).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('parseJsonc', () => {
|
|
it('returns parsed jsonc', () => {
|
|
expect(parseJson(validJsoncString, 'renovate.jsonc')).toEqual({
|
|
name: 'John Doe',
|
|
age: 30,
|
|
city: 'New York',
|
|
});
|
|
});
|
|
|
|
it('throws error for invalid jsonc', () => {
|
|
expect(() => parseJson(invalidJsonString, 'renovate.jsonc')).toThrow(
|
|
'Parse error: Expected comma on line 6 column 47',
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('getInheritedOrGlobal', () => {
|
|
beforeEach(() => {
|
|
GlobalConfig.reset();
|
|
InheritConfig.reset();
|
|
});
|
|
|
|
it('returns undefined if not set', () => {
|
|
expect(getInheritedOrGlobal('configFileNames')).toBeUndefined();
|
|
});
|
|
|
|
it('returns inherited value if only inherited value is set', () => {
|
|
InheritConfig.set({
|
|
configFileNames: ['inherited'],
|
|
});
|
|
expect(getInheritedOrGlobal('configFileNames')).toEqual(['inherited']);
|
|
});
|
|
|
|
it('returns global value if only global value is set', () => {
|
|
GlobalConfig.set({
|
|
configFileNames: ['global'],
|
|
});
|
|
expect(getInheritedOrGlobal('configFileNames')).toEqual(['global']);
|
|
});
|
|
|
|
it('returns inherited value - when both global + inherited are set', () => {
|
|
InheritConfig.set({
|
|
configFileNames: ['inherited'],
|
|
});
|
|
GlobalConfig.set({
|
|
configFileNames: ['global'],
|
|
});
|
|
expect(getInheritedOrGlobal('configFileNames')).toEqual(['inherited']);
|
|
});
|
|
|
|
// is not possiblle as config validation will error out: only for coverage
|
|
it('handles null inherited values', () => {
|
|
InheritConfig.set({
|
|
configFileNames: null as never,
|
|
});
|
|
GlobalConfig.set({
|
|
configFileNames: ['global'],
|
|
});
|
|
expect(getInheritedOrGlobal('configFileNames')).toBeNull();
|
|
});
|
|
|
|
// is not possiblle as config validation will error out: only for coverage
|
|
it('handles undefined inherited values', () => {
|
|
InheritConfig.set({
|
|
configFileNames: undefined,
|
|
});
|
|
GlobalConfig.set({
|
|
configFileNames: ['global'],
|
|
});
|
|
expect(getInheritedOrGlobal('configFileNames')).toBeUndefined();
|
|
});
|
|
|
|
describe('when requesting onboardingAutoCloseAge, do not allow inherit config to override global config', () => {
|
|
it('returns inherited value when inherited < global', () => {
|
|
GlobalConfig.set({
|
|
onboardingAutoCloseAge: 10,
|
|
});
|
|
InheritConfig.set({
|
|
onboardingAutoCloseAge: 5,
|
|
});
|
|
expect(getInheritedOrGlobal('onboardingAutoCloseAge')).toBe(5);
|
|
});
|
|
|
|
it('returns global value when inherited > global value', () => {
|
|
GlobalConfig.set({
|
|
onboardingAutoCloseAge: 5,
|
|
});
|
|
InheritConfig.set({
|
|
onboardingAutoCloseAge: 10,
|
|
});
|
|
expect(getInheritedOrGlobal('onboardingAutoCloseAge')).toBe(5);
|
|
});
|
|
|
|
it('returns inherited value when inherited == global', () => {
|
|
GlobalConfig.set({
|
|
onboardingAutoCloseAge: 5,
|
|
});
|
|
InheritConfig.set({
|
|
onboardingAutoCloseAge: 5,
|
|
});
|
|
expect(getInheritedOrGlobal('onboardingAutoCloseAge')).toBe(5);
|
|
});
|
|
|
|
it('returns inherited value when global value is not set', () => {
|
|
GlobalConfig.set({
|
|
onboardingAutoCloseAge: undefined,
|
|
});
|
|
InheritConfig.set({
|
|
onboardingAutoCloseAge: 10,
|
|
});
|
|
expect(getInheritedOrGlobal('onboardingAutoCloseAge')).toBe(10);
|
|
});
|
|
|
|
it('returns global value when inherited value is not set', () => {
|
|
GlobalConfig.set({
|
|
onboardingAutoCloseAge: 10,
|
|
});
|
|
expect(getInheritedOrGlobal('onboardingAutoCloseAge')).toBe(10);
|
|
});
|
|
});
|
|
});
|
|
});
|