Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(workers/reconfigure): update code structure #33340

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/workers/repository/finalize/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { platform } from '../../../modules/platform';
import * as repositoryCache from '../../../util/cache/repository';
import { clearRenovateRefs } from '../../../util/git';
import { PackageFiles } from '../package-files';
import { validateReconfigureBranch } from '../reconfigure';
import { checkReconfigureBranch } from '../reconfigure';
import { pruneStaleBranches } from './prune';
import {
runBranchSummary,
Expand All @@ -16,7 +16,7 @@ export async function finalizeRepo(
config: RenovateConfig,
branchList: string[],
): Promise<void> {
await validateReconfigureBranch(config);
await checkReconfigureBranch(config);
await repositoryCache.saveCache();
await pruneStaleBranches(config, branchList);
await ensureIssuesClosing();
Expand Down
2 changes: 1 addition & 1 deletion lib/workers/repository/finalize/prune.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { scm } from '../../../modules/platform/scm';
import { getBranchList, setUserRepoConfig } from '../../../util/git';
import { escapeRegExp, regEx } from '../../../util/regex';
import { uniqueStrings } from '../../../util/string';
import { getReconfigureBranchName } from '../reconfigure';
import { getReconfigureBranchName } from '../reconfigure/utils';

async function cleanUpBranches(
config: RenovateConfig,
Expand Down
228 changes: 14 additions & 214 deletions lib/workers/repository/reconfigure/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,242 +1,42 @@
import { mock } from 'jest-mock-extended';
import type { RenovateConfig } from '../../../../test/util';
import { fs, git, mocked, partial, platform, scm } from '../../../../test/util';
import { logger, mocked, scm } from '../../../../test/util';
import { GlobalConfig } from '../../../config/global';
import { logger } from '../../../logger';
import type { Pr } from '../../../modules/platform/types';
import * as _cache from '../../../util/cache/repository';
import type { LongCommitSha } from '../../../util/git/types';
import * as _merge from '../init/merge';
import { validateReconfigureBranch } from '.';
import * as _validate from './validate';
import { checkReconfigureBranch } from '.';

jest.mock('../../../util/cache/repository');
jest.mock('../../../util/fs');
jest.mock('../../../util/git');
jest.mock('../init/merge');
jest.mock('./validate');

const cache = mocked(_cache);
const merge = mocked(_merge);
const validate = mocked(_validate);

describe('workers/repository/reconfigure/index', () => {
const config: RenovateConfig = {
branchPrefix: 'prefix/',
baseBranch: 'base',
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
configValidation: 'renovate/config-validation',
}),
};

beforeEach(() => {
config.repository = 'some/repo';
merge.detectConfigFile.mockResolvedValue('renovate.json');
scm.branchExists.mockResolvedValue(true);
cache.getCache.mockReturnValue({});
git.getBranchCommit.mockReturnValue('sha' as LongCommitSha);
fs.readLocalFile.mockResolvedValue(null);
platform.getBranchStatusCheck.mockResolvedValue(null);
GlobalConfig.reset();
scm.branchExists.mockResolvedValue(true);
validate.validateReconfigureBranch.mockResolvedValue(undefined);
});

it('no effect when running with platform=local', async () => {
GlobalConfig.set({ platform: 'local' });
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith(
await checkReconfigureBranch(config);
expect(logger.logger.debug).toHaveBeenCalledWith(
'Not attempting to reconfigure when running with local platform',
);
});

it('no effect on repo with no reconfigure branch', async () => {
scm.branchExists.mockResolvedValueOnce(false);
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith('No reconfigure branch found');
});

it('logs error if config file search fails', async () => {
const err = new Error();
merge.detectConfigFile.mockRejectedValueOnce(err as never);
await validateReconfigureBranch(config);
expect(logger.error).toHaveBeenCalledWith(
{ err },
'Error while searching for config file in reconfigure branch',
);
});

it('throws error if config file not found in reconfigure branch', async () => {
merge.detectConfigFile.mockResolvedValue(null);
await validateReconfigureBranch(config);
expect(logger.warn).toHaveBeenCalledWith(
'No config file found in reconfigure branch',
);
});

it('logs error if config file is unreadable', async () => {
const err = new Error();
fs.readLocalFile.mockRejectedValueOnce(err as never);
await validateReconfigureBranch(config);
expect(logger.error).toHaveBeenCalledWith(
{ err },
'Error while reading config file',
);
});

it('throws error if config file is empty', async () => {
await validateReconfigureBranch(config);
expect(logger.warn).toHaveBeenCalledWith('Empty or invalid config file');
});

it('throws error if config file content is invalid', async () => {
fs.readLocalFile.mockResolvedValueOnce(`
{
"name":
}
`);
await validateReconfigureBranch(config);
expect(logger.error).toHaveBeenCalledWith(
{ err: expect.any(Object) },
'Error while parsing config file',
);
expect(platform.setBranchStatus).toHaveBeenCalledWith({
branchName: 'prefix/reconfigure',
context: 'renovate/config-validation',
description: 'Validation Failed - Unparsable config file',
state: 'red',
});
});

it('handles failed validation', async () => {
fs.readLocalFile.mockResolvedValueOnce(`
{
"enabledManagers": ["docker"]
}
`);
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith(
{ errors: expect.any(String) },
'Validation Errors',
);
expect(platform.setBranchStatus).toHaveBeenCalledWith({
branchName: 'prefix/reconfigure',
context: 'renovate/config-validation',
description: 'Validation Failed',
state: 'red',
});
});

it('adds comment if reconfigure PR exists', async () => {
fs.readLocalFile.mockResolvedValueOnce(`
{
"enabledManagers": ["docker"]
}
`);
platform.findPr.mockResolvedValueOnce(mock<Pr>({ number: 1 }));
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith(
{ errors: expect.any(String) },
'Validation Errors',
);
expect(platform.setBranchStatus).toHaveBeenCalled();
expect(platform.ensureComment).toHaveBeenCalled();
});

it('handles successful validation', async () => {
const pJson = `
{
"renovate": {
"enabledManagers": ["npm"]
}
}
`;
merge.detectConfigFile.mockResolvedValue('package.json');
fs.readLocalFile.mockResolvedValueOnce(pJson).mockResolvedValueOnce(pJson);
await validateReconfigureBranch(config);
expect(platform.setBranchStatus).toHaveBeenCalledWith({
branchName: 'prefix/reconfigure',
context: 'renovate/config-validation',
description: 'Validation Successful',
state: 'green',
});
});

it('skips adding status check if statusCheckNames.configValidation is null', async () => {
cache.getCache.mockReturnValueOnce({
reconfigureBranchCache: {
reconfigureBranchSha: 'new-sha',
isConfigValid: false,
},
});

await validateReconfigureBranch({
...config,
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
configValidation: null,
}),
});
expect(logger.debug).toHaveBeenCalledWith(
'Status check is null or an empty string, skipping status check addition.',
);
expect(platform.setBranchStatus).not.toHaveBeenCalled();
});

it('skips adding status check if statusCheckNames.configValidation is empty string', async () => {
cache.getCache.mockReturnValueOnce({
reconfigureBranchCache: {
reconfigureBranchSha: 'new-sha',
isConfigValid: false,
},
});

await validateReconfigureBranch({
...config,
statusCheckNames: partial<RenovateConfig['statusCheckNames']>({
configValidation: '',
}),
});
expect(logger.debug).toHaveBeenCalledWith(
'Status check is null or an empty string, skipping status check addition.',
);
expect(platform.setBranchStatus).not.toHaveBeenCalled();
});

it('skips validation if cache is valid', async () => {
cache.getCache.mockReturnValueOnce({
reconfigureBranchCache: {
reconfigureBranchSha: 'sha',
isConfigValid: false,
},
});
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith(
'Skipping validation check as branch sha is unchanged',
);
});

it('skips validation if status check present', async () => {
cache.getCache.mockReturnValueOnce({
reconfigureBranchCache: {
reconfigureBranchSha: 'new_sha',
isConfigValid: false,
},
});
platform.getBranchStatusCheck.mockResolvedValueOnce('green');
await validateReconfigureBranch(config);
expect(logger.debug).toHaveBeenCalledWith(
'Skipping validation check because status check already exists.',
await checkReconfigureBranch(config);
expect(logger.logger.debug).toHaveBeenCalledWith(
'No reconfigure branch found',
);
});

it('handles non-default config file', async () => {
merge.detectConfigFile.mockResolvedValue('.renovaterc');
fs.readLocalFile.mockResolvedValueOnce(`
{
"enabledManagers": ["npm",]
}
`);
await validateReconfigureBranch(config);
expect(platform.setBranchStatus).toHaveBeenCalledWith({
branchName: 'prefix/reconfigure',
context: 'renovate/config-validation',
description: 'Validation Successful',
state: 'green',
});
it('validates reconfigure branch', async () => {
await expect(checkReconfigureBranch(config)).toResolve();
});
});
Loading
Loading