renovate/test/workers/branch.spec.js
Rhys Arkins 24955fab77 Support GitLab merge conflict rebasing (#335)
Closes #132 

* Add gitlab deleteBranch

* Add GitLab rebasing logic to branch worker

* Add GitLab getBranch

* Record user email during initRepo

* Add canRebase logic

* lint fix

* Add test
2017-06-22 11:56:23 +02:00

373 lines
17 KiB
JavaScript

const branchWorker = require('../../lib/workers/branch');
const prWorker = require('../../lib/workers/pr');
const npmHelper = require('../../lib/helpers/npm');
const yarnHelper = require('../../lib/helpers/yarn');
const defaultConfig = require('../../lib/config/defaults').getConfig();
const packageJsonHelper = require('../../lib/helpers/package-json');
const bunyan = require('bunyan');
const logger = bunyan.createLogger({
name: 'test',
stream: process.stdout,
level: 'fatal',
});
jest.mock('../../lib/helpers/yarn');
jest.mock('../../lib/helpers/package-json');
describe('workers/branch', () => {
describe('getParentBranch(branchName, config)', () => {
let config;
const branchName = 'foo';
beforeEach(() => {
config = {
platform: 'github',
api: {
branchExists: jest.fn(() => true),
deleteBranch: jest.fn(),
getBranchPr: jest.fn(),
getBranchStatus: jest.fn(),
isBranchStale: jest.fn(() => false),
},
};
});
it('returns undefined if branch does not exist', async () => {
config.api.branchExists.mockReturnValue(false);
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
undefined
);
});
it('returns branchName if no PR', async () => {
config.api.getBranchPr.mockReturnValue(null);
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
branchName
);
});
it('returns branchName if does not need rebaseing', async () => {
config.api.getBranchPr.mockReturnValue({
isUnmergeable: false,
});
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
branchName
);
});
it('returns branchName if unmergeable and cannot rebase', async () => {
config.api.getBranchPr.mockReturnValue({
isUnmergeable: true,
canRebase: false,
});
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
branchName
);
});
it('returns undefined if unmergeable and can rebase', async () => {
config.api.getBranchPr.mockReturnValue({
isUnmergeable: true,
canRebase: true,
});
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
undefined
);
});
it('returns undefined if unmergeable and can rebase (gitlab)', async () => {
config.platform = 'gitlab';
config.api.getBranchPr.mockReturnValue({
isUnmergeable: true,
canRebase: true,
});
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
undefined
);
expect(config.api.deleteBranch.mock.calls.length).toBe(1);
});
it('returns branchName if automerge branch-push and not stale', async () => {
config.automergeEnabled = true;
config.automergeType = 'branch-push';
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
branchName
);
});
it('returns undefined if automerge branch-push and stale', async () => {
config.automergeEnabled = true;
config.automergeType = 'branch-push';
config.api.isBranchStale.mockReturnValueOnce(true);
expect(await branchWorker.getParentBranch(branchName, config)).toBe(
undefined
);
});
});
describe('ensureBranch(config)', () => {
let config;
beforeEach(() => {
packageJsonHelper.setNewValue = jest.fn();
branchWorker.getParentBranch = jest.fn();
npmHelper.getLockFile = jest.fn();
yarnHelper.getLockFile = jest.fn();
yarnHelper.maintainLockFile = jest.fn();
config = Object.assign({}, defaultConfig);
config.api = {};
config.api.getFileContent = jest.fn();
config.api.branchExists = jest.fn();
config.api.commitFilesToBranch = jest.fn();
config.api.getFileContent.mockReturnValueOnce('old content');
config.api.getBranchStatus = jest.fn();
config.depName = 'dummy';
config.currentVersion = '1.0.0';
config.newVersion = '1.1.0';
config.newVersionMajor = 1;
});
it('returns if new content matches old', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('old content');
config.api.branchExists.mockReturnValueOnce(false);
expect(await branchWorker.ensureBranch([config])).toBe(false);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(0);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(0);
});
it('commits one file if no yarn lock or package-lock.json found', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
config.api.branchExists.mockReturnValueOnce(true);
expect(await branchWorker.ensureBranch([config])).toBe(true);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('returns true if automerging pr', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
config.api.branchExists.mockReturnValueOnce(true);
config.automergeEnabled = true;
config.automergeType = 'pr';
expect(await branchWorker.ensureBranch([config])).toBe(true);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('automerges successful branches', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
config.api.branchExists.mockReturnValueOnce(true);
config.api.getBranchStatus.mockReturnValueOnce('success');
config.api.mergeBranch = jest.fn();
config.automergeEnabled = true;
config.automergeType = 'branch-push';
expect(await branchWorker.ensureBranch([config])).toBe(true);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(config.api.getBranchStatus.mock.calls.length).toBe(1);
expect(config.api.mergeBranch.mock).toMatchSnapshot();
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('skips automerge if status not success', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
config.api.branchExists.mockReturnValueOnce(true);
config.api.getBranchStatus.mockReturnValueOnce('pending');
config.api.mergeBranch = jest.fn();
config.automergeEnabled = true;
config.automergeType = 'branch-push';
expect(await branchWorker.ensureBranch([config])).toBe(true);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(config.api.getBranchStatus.mock.calls.length).toBe(1);
expect(config.api.mergeBranch.mock.calls.length).toBe(0);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('throws if automerge throws', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
config.api.branchExists.mockReturnValueOnce(true);
config.api.getBranchStatus.mockReturnValueOnce('success');
config.automergeEnabled = true;
config.automergeType = 'branch-push';
config.api.mergeBranch = jest.fn(() => {
throw new Error('automerge failed');
});
let e;
try {
await branchWorker.ensureBranch([config]);
} catch (err) {
e = err;
}
expect(e).toMatchSnapshot();
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(config.api.getBranchStatus.mock.calls.length).toBe(1);
expect(config.api.mergeBranch.mock).toMatchSnapshot();
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('commits two files if yarn lock found', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
yarnHelper.getLockFile.mockReturnValueOnce('non null response');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
await branchWorker.ensureBranch([config]);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(2);
});
it('commits two files if package lock found', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
npmHelper.getLockFile.mockReturnValueOnce('non null response');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
await branchWorker.ensureBranch([config]);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(2);
});
it('commits three files if yarn lock and package lock found', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
npmHelper.getLockFile.mockReturnValueOnce('non null response');
yarnHelper.getLockFile.mockReturnValueOnce('non null response');
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
await branchWorker.ensureBranch([config]);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(3);
});
it('throws an error if no yarn lock generation possible', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
yarnHelper.getLockFile.mockImplementationOnce(() => {
throw new Error('yarn not found');
});
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
let err;
try {
await branchWorker.ensureBranch([config]);
} catch (e) {
err = e;
}
expect(err.message).toBe('Could not generate new yarn.lock file');
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(0);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
});
it('throws an error if no package lock generation possible', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
npmHelper.getLockFile.mockImplementationOnce(() => {
throw new Error('no package lock generated');
});
packageJsonHelper.setNewValue.mockReturnValueOnce('new content');
let err;
try {
await branchWorker.ensureBranch([config]);
} catch (e) {
err = e;
}
expect(err.message).toBe('Could not generate new package-lock.json file');
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(1);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(1);
expect(npmHelper.getLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
});
it('maintains lock files if needing updates', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
yarnHelper.maintainLockFile.mockReturnValueOnce('non null response');
config.upgradeType = 'maintainYarnLock';
await branchWorker.ensureBranch([config]);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(0);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(0);
expect(npmHelper.getLockFile.mock.calls.length).toBe(0);
expect(yarnHelper.maintainLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls[0][1].length).toBe(1);
});
it('skips maintaining lock files if no updates', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
config.upgradeType = 'maintainYarnLock';
await branchWorker.ensureBranch([config]);
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(0);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(0);
expect(npmHelper.getLockFile.mock.calls.length).toBe(0);
expect(yarnHelper.maintainLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
});
it('throws error if cannot maintain yarn.lock file', async () => {
branchWorker.getParentBranch.mockReturnValueOnce('dummy branch');
config.upgradeType = 'maintainYarnLock';
yarnHelper.maintainLockFile.mockImplementationOnce(() => {
throw new Error('yarn not found');
});
let err;
try {
await branchWorker.ensureBranch([config]);
} catch (e) {
err = e;
}
expect(err.message).toBe('Could not maintain yarn.lock file');
expect(branchWorker.getParentBranch.mock.calls.length).toBe(1);
expect(packageJsonHelper.setNewValue.mock.calls.length).toBe(0);
expect(yarnHelper.getLockFile.mock.calls.length).toBe(0);
expect(npmHelper.getLockFile.mock.calls.length).toBe(0);
expect(yarnHelper.maintainLockFile.mock.calls.length).toBe(1);
expect(config.api.commitFilesToBranch.mock.calls.length).toBe(0);
});
});
describe('updateBranch(upgrades)', () => {
let config;
beforeEach(() => {
config = Object.assign({}, defaultConfig);
config.api = {
checkForClosedPr: jest.fn(),
};
branchWorker.ensureBranch = jest.fn();
prWorker.ensurePr = jest.fn();
});
it('returns immediately if closed PR found', async () => {
config.api.checkForClosedPr.mockReturnValue(true);
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(0);
});
it('does not return immediately if recreateClosed true', async () => {
config.api.checkForClosedPr.mockReturnValue(true);
config.recreateClosed = true;
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
});
it('pins', async () => {
config.upgradeType = 'pin';
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
});
it('majors', async () => {
config.upgradeType = 'major';
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
});
it('minors', async () => {
config.upgradeType = 'minor';
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(1);
});
it('handles errors', async () => {
config.api.checkForClosedPr = jest.fn(() => {
throw new Error('oops');
});
await branchWorker.updateBranch([config], logger);
expect(branchWorker.ensureBranch.mock.calls.length).toBe(0);
});
});
});