diff --git a/lib/index.js b/lib/index.js index 2ffa1449346c84ad31be2c00994dabf0da9fe2ff..abc9a603bfef74c76377b9714ba3c22a88b48aad 100644 --- a/lib/index.js +++ b/lib/index.js @@ -136,6 +136,6 @@ async function findPackageFiles(config) { async function processPackageFiles(repo) { for (const packageFile of repo.packageFiles) { const cascadedConfig = configParser.getCascadedConfig(repo, packageFile); - await worker(repo.repository, packageFile.fileName, cascadedConfig); + await worker.processPackageFile(repo.repository, packageFile.fileName, cascadedConfig); } } diff --git a/lib/worker.js b/lib/worker.js index f916217565ab72a5b3e8ca9cfdc62b2d9c21ffb0..e85a361d31033d6be7697ca887d41e8a3a4776b9 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -11,10 +11,15 @@ const branchWorker = require('./workers/branch'); let config; -module.exports = renovate; +module.exports = { + processPackageFile, + findUpgrades, + processUpgradesSequentially, + updateDependency, +}; // This function manages the queue per-package file -async function renovate(repoName, packageFile, packageConfig) { +async function processPackageFile(repoName, packageFile, packageConfig) { // Initialize globals config = Object.assign({}, packageConfig); config.packageFile = packageFile; @@ -47,19 +52,19 @@ async function renovate(repoName, packageFile, packageConfig) { dependencies = dependencies.filter(dependency => config.ignoreDeps.indexOf(dependency.depName) === -1); // Find all upgrades for remaining dependencies - const upgrades = await findUpgrades(dependencies); + const upgrades = await findUpgrades(dependencies, config); // Process all upgrades sequentially - await processUpgradesSequentially(upgrades); + await processUpgradesSequentially(config, upgrades); logger.info(`${repoName} ${packageFile} done`); } -async function findUpgrades(dependencies) { +async function findUpgrades(dependencies, inputConfig) { const allUpgrades = []; // findDepUpgrades can add more than one upgrade to allUpgrades async function findDepUpgrades(dep) { const npmDependency = await npmApi.getDependency(dep.depName); const upgrades = - await versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, config); + await versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, inputConfig); if (upgrades.length > 0) { logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`); upgrades.forEach((upgrade) => { @@ -76,7 +81,7 @@ async function findUpgrades(dependencies) { return allUpgrades; } -async function processUpgradesSequentially(upgrades) { +async function processUpgradesSequentially(baseConfig, upgrades) { if (Object.keys(upgrades).length) { logger.verbose('Processing upgrades'); } else { @@ -87,15 +92,13 @@ async function processUpgradesSequentially(upgrades) { // 1. Reduce chances of GitHub API rate limiting // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency for (const upgrade of upgrades) { - await updateDependency(upgrade); + await module.exports.updateDependency(Object.assign({}, baseConfig, upgrade)); } } async function updateDependency(upgrade) { - // Expand upgrade params - const depName = upgrade.depName; // Helpers for templating - const params = Object.assign({}, config, upgrade); + const params = Object.assign({}, upgrade); if (upgrade.upgradeType === 'pin') { params.isPin = true; } else if (upgrade.upgradeType === 'major') { @@ -104,18 +107,18 @@ async function updateDependency(upgrade) { params.isMinor = true; } // Use templates to generate strings - const branchName = handlebars.compile(config.branchName)(params); - const prTitle = handlebars.compile(config.prTitle)(params); + const branchName = handlebars.compile(params.branchName)(params); + const prTitle = handlebars.compile(params.prTitle)(params); try { - if (!config.recreateClosed && await config.api.checkForClosedPr(branchName, prTitle)) { - logger.verbose(`Skipping ${depName} upgrade as matching closed PR already existed`); + if (!params.recreateClosed && await params.api.checkForClosedPr(branchName, prTitle)) { + logger.verbose(`Skipping ${params.depName} upgrade as matching closed PR already existed`); return; } await branchWorker.ensureBranch(params); await prWorker.ensurePr(params); } catch (error) { - logger.error(`Error updating dependency ${depName}: ${error}`); + logger.error(`Error updating dependency ${params.depName}: ${error}`); // Don't throw here - we don't want to stop the other renovations } } diff --git a/test/worker.spec.js b/test/worker.spec.js index 1564f4fca341961ea02878661baea265b064787d..efa97a014df0649feb6ee821ed22e4db74416cdc 100644 --- a/test/worker.spec.js +++ b/test/worker.spec.js @@ -1,5 +1,108 @@ -require('../lib/worker'); +const logger = require('winston'); +const worker = require('../lib/worker'); +const branchWorker = require('../lib/workers/branch'); +const prWorker = require('../lib/workers/pr'); +const defaultConfig = require('../lib/config/defaults').getConfig(); +const npmApi = require('../lib/api/npm'); +const versionsHelper = require('../lib/helpers/versions'); -it('placeholder', () => { - // TODO: write tests for this module - this is here so the file shows up in coverage +logger.remove(logger.transports.Console); + +jest.mock('../lib/workers/branch'); +jest.mock('../lib/workers/pr'); +jest.mock('../lib/api/npm'); +jest.mock('../lib/helpers/versions'); + +describe('worker', () => { + describe('updateDependency(upgrade)', () => { + 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 worker.updateDependency(config); + 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 worker.updateDependency(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(1); + }); + it('pins', async () => { + config.upgradeType = 'pin'; + await worker.updateDependency(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(1); + }); + it('majors', async () => { + config.upgradeType = 'major'; + await worker.updateDependency(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(1); + }); + it('minors', async () => { + config.upgradeType = 'minor'; + await worker.updateDependency(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(1); + }); + it('handles errors', async () => { + config.api.checkForClosedPr = jest.fn(() => { + throw new Error('oops'); + }); + await worker.updateDependency(config); + expect(branchWorker.ensureBranch.mock.calls.length).toBe(0); + }); + }); + describe('processUpgradesSequentially(baseConfig, upgrades)', () => { + let config; + beforeEach(() => { + config = {}; + worker.updateDependency = jest.fn(); + }); + it('handles zero upgrades', async () => { + await worker.processUpgradesSequentially(config, []); + expect(worker.updateDependency.mock.calls.length).toBe(0); + }); + it('handles non-zero upgrades', async () => { + await worker.processUpgradesSequentially(config, [{}, {}]); + expect(worker.updateDependency.mock.calls.length).toBe(2); + }); + }); + describe('findUpgrades(dependencies, config)', () => { + let config; + beforeEach(() => { + config = {}; + worker.updateDependency = jest.fn(); + }); + it('handles null', async () => { + const allUpgrades = await worker.findUpgrades([], config); + expect(allUpgrades).toMatchObject([]); + }); + it('handles one dep', async () => { + const dep = { + depName: 'foo', + currentVersion: '1.0.0', + }; + const upgrade = { newVersion: '1.1.0' }; + npmApi.getDependency = jest.fn(() => ({})); + versionsHelper.determineUpgrades = jest.fn(() => [upgrade]); + const allUpgrades = await worker.findUpgrades([dep], config); + expect(allUpgrades).toMatchObject([Object.assign({}, dep, upgrade)]); + }); + it('handles no upgrades', async () => { + const dep = { + depName: 'foo', + currentVersion: '1.0.0', + }; + npmApi.getDependency = jest.fn(() => ({})); + versionsHelper.determineUpgrades = jest.fn(() => []); + const allUpgrades = await worker.findUpgrades([dep], config); + expect(allUpgrades).toMatchObject([]); + }); + }); });