diff --git a/lib/platform/gitlab/gl-got-wrapper.js b/lib/platform/gitlab/gl-got-wrapper.js index 4a17c5989b63e67175285279d4692ce32a272d39..b94465521b8c1f3c728cf89be0805de8b0a6a8d8 100644 --- a/lib/platform/gitlab/gl-got-wrapper.js +++ b/lib/platform/gitlab/gl-got-wrapper.js @@ -33,7 +33,7 @@ async function get(path, options, retries = 5) { (await get(linkHeader.next.url, opts, retries)).body ); } - } catch (err) { + } catch (err) /* istanbul ignore next */ { logger.warn({ err }, 'Pagination error'); } } diff --git a/lib/platform/gitlab/index.js b/lib/platform/gitlab/index.js index 891cb81d9df1e2f9a68b1f612a0437b34835a82f..63978004abc493b59c98ee04b22908971997239c 100644 --- a/lib/platform/gitlab/index.js +++ b/lib/platform/gitlab/index.js @@ -4,11 +4,8 @@ const is = require('@sindresorhus/is'); const get = require('./gl-got-wrapper'); const hostRules = require('../../util/host-rules'); const GitStorage = require('../git/storage'); -const Storage = require('./storage'); -let config = { - storage: new Storage(), -}; +let config = {}; module.exports = { getRepos, @@ -124,26 +121,23 @@ async function initRepo({ repository, token, endpoint, gitFs, localDir }) { config.email = (await get(`user`)).body.email; logger.debug('Bot email=' + config.email); delete config.prList; - // istanbul ignore if - if (config.gitFs) { - logger.debug('Enabling Git FS'); - const { host } = URL.parse(opts.endpoint); - const url = GitStorage.getUrl({ - gitFs, - auth: 'oauth2:' + opts.token, - hostname: host || 'gitlab.com', - repository, - }); - config.storage = new GitStorage(); - await config.storage.initRepo({ - ...config, - url, - }); - } else { - config.storage = new Storage(); - await config.storage.initRepo(config); - } - await Promise.all([getPrList(), getFileList()]); + // Always gitFs + logger.debug('Enabling Git FS'); + const { hostname, protocol } = URL.parse( + opts.endpoint || 'https://gitlab.com' + ); + const detectedGitFs = protocol.substring(0, protocol.length - 1); // strip the trailing ':' + const url = GitStorage.getUrl({ + gitFs: gitFs || detectedGitFs, + auth: 'oauth2:' + opts.token, + hostname, + repository, + }); + config.storage = new GitStorage(); + await config.storage.initRepo({ + ...config, + url, + }); } catch (err) /* istanbul ignore next */ { logger.debug('Caught initRepo error'); if (err.message.includes('HEAD is not a symbolic ref')) { @@ -193,6 +187,7 @@ function branchExists(branchName) { // Returns the Pull Request for a branch. Null if not exists. async function getBranchPr(branchName) { logger.debug(`getBranchPr(${branchName})`); + // istanbul ignore if if (!(await branchExists(branchName))) { return null; } @@ -221,39 +216,18 @@ function isBranchStale(branchName) { return config.storage.isBranchStale(branchName); } -async function commitFilesToBranch( +function commitFilesToBranch( branchName, files, message, parentBranch = config.baseBranch ) { - // GitLab does not support push with GitFs token - // See https://gitlab.com/gitlab-org/gitlab-ce/issues/18106 - let storage = config.storage; - // istanbul ignore if - if (config.gitFs === 'http' || config.gitFs === 'https') { - storage = new Storage(); - storage.initRepo(config); - } - const res = await storage.commitFilesToBranch( + return config.storage.commitFilesToBranch( branchName, files, message, parentBranch ); - if (config.gitFs !== 'ssh') { - // Reopen PR if it previousluy existed and was closed by GitLab when we deleted branch - const pr = await getBranchPr(branchName); - // istanbul ignore if - if (pr) { - logger.debug('Reopening PR'); - await reopenPr(pr.number); - // Close and repoen MR again due to known GitLab bug https://gitlab.com/gitlab-org/gitlab-ce/issues/41545 - await closePr(pr.number); - await reopenPr(pr.number); - } - } - return res; } function getFile(filePath, branchName) { @@ -732,15 +706,6 @@ async function getPrFiles(mrNo) { return files.map(f => f.new_path); } -// istanbul ignore next -async function reopenPr(iid) { - await get.put(`projects/${config.repository}/merge_requests/${iid}`, { - body: { - state_event: 'reopen', - }, - }); -} - // istanbul ignore next async function closePr(iid) { await get.put(`projects/${config.repository}/merge_requests/${iid}`, { diff --git a/lib/platform/gitlab/storage.js b/lib/platform/gitlab/storage.js deleted file mode 100644 index d70864d4ba3ef8b31a5495162475bc9d35962dc5..0000000000000000000000000000000000000000 --- a/lib/platform/gitlab/storage.js +++ /dev/null @@ -1,304 +0,0 @@ -const get = require('./gl-got-wrapper'); - -function urlEscape(str) { - return str ? str.replace(/\//g, '%2F') : str; -} - -class Storage { - constructor() { - // config - let config = {}; - // cache - let baseCommitSHA = null; - let branchFiles = {}; - - Object.assign(this, { - initRepo, - cleanRepo, - getRepoStatus: () => ({}), - branchExists, - commitFilesToBranch, - createBranch, - deleteBranch, - getAllRenovateBranches, - getBranchCommit, - getBranchLastCommitTime, - getCommitMessages, - getFile, - getFileList, - isBranchStale, - mergeBranch, - setBaseBranch, - }); - - function initRepo(args) { - cleanRepo(); - config = { ...args }; - } - - function cleanRepo() { - baseCommitSHA = null; - branchFiles = {}; - } - - // Branch - - // Returns true if branch exists, otherwise false - async function branchExists(branchName) { - logger.debug(`Checking if branch exists: ${branchName}`); - try { - const url = `projects/${ - config.repository - }/repository/branches/${urlEscape(branchName)}`; - const res = await get(url); - if (res.statusCode === 200) { - logger.debug('Branch exists'); - return true; - } - // This probably shouldn't happen - logger.debug("Branch doesn't exist"); - return false; - } catch (error) { - if (error.statusCode === 404) { - // If file not found, then return false - logger.debug("Branch doesn't exist"); - return false; - } - // Propagate if it's any other error - throw error; - } - } - - async function getAllRenovateBranches(branchPrefix) { - logger.debug(`getAllRenovateBranches(${branchPrefix})`); - - const search = urlEscape(`^${branchPrefix}`); - - const allBranches = await get( - `projects/${config.repository}/repository/branches?search=${search}`, - { paginate: true } - ); - return allBranches.body - .map(branch => branch.name) - .filter(branchName => branchName.startsWith(branchPrefix)); - } - - async function isBranchStale(branchName) { - logger.debug(`isBranchStale(${branchName})`); - const branchDetails = await getBranchDetails(branchName); - logger.trace({ branchDetails }, 'branchDetails'); - const parentSha = branchDetails.body.commit.parent_ids[0]; - logger.debug(`parentSha=${parentSha}`); - const baseSHA = await getBaseCommitSHA(); - logger.debug(`baseSHA=${baseSHA}`); - // Return true if the SHAs don't match - return parentSha !== baseSHA; - } - - // Add a new commit, create branch if not existing - async function commitFilesToBranch( - branchName, - files, - message, - parentBranch = config.baseBranch - ) { - logger.debug( - `commitFilesToBranch('${branchName}', files, message, '${parentBranch})'` - ); - const opts = { - body: { - branch: branchName, - commit_message: message, - start_branch: parentBranch, - actions: [], - }, - }; - // istanbul ignore if - if (global.gitAuthor) { - opts.body.author_name = global.gitAuthor.name; - opts.body.author_email = global.gitAuthor.email; - } - for (const file of files) { - const action = { - file_path: file.name, - content: Buffer.from(file.contents).toString('base64'), - encoding: 'base64', - }; - action.action = (await getFile(file.name)) ? 'update' : 'create'; - opts.body.actions.push(action); - } - let res = 'created'; - try { - if (await branchExists(branchName)) { - logger.debug('Deleting existing branch'); - await deleteBranch(branchName); - res = 'updated'; - } - } catch (err) { - // istanbul ignore next - logger.info(`Ignoring branch deletion failure`); - } - logger.debug('Adding commits'); - await get.post(`projects/${config.repository}/repository/commits`, opts); - return res; - } - - async function createBranch(branchName, sha) { - await get.post( - `projects/${config.repository}/repository/branches?branch=${urlEscape( - branchName - )}&ref=${sha}` - ); - } - - async function deleteBranch(branchName) { - await get.delete( - `projects/${config.repository}/repository/branches/${urlEscape( - branchName - )}` - ); - } - - // Search - - // Get full file list - async function getFileList(branchName = config.baseBranch) { - if (branchFiles[branchName]) { - return branchFiles[branchName]; - } - try { - let url = `projects/${ - config.repository - }/repository/tree?ref=${branchName}&per_page=100`; - if (!(process.env.RENOVATE_DISABLE_FILE_RECURSION === 'true')) { - url += '&recursive=true'; - } - const res = await get(url, { paginate: true }); - branchFiles[branchName] = res.body - .filter(item => item.type === 'blob' && item.mode !== '120000') - .map(item => item.path) - .sort(); - logger.debug( - `Retrieved fileList with length ${branchFiles[branchName].length}` - ); - } catch (err) { - logger.info('Error retrieving git tree - no files detected'); - branchFiles[branchName] = []; - } - return branchFiles[branchName]; - } - - // Generic File operations - - async function getFile(filePath, branchName) { - logger.debug(`getFile(filePath=${filePath}, branchName=${branchName})`); - if (!branchName || branchName === config.baseBranch) { - if ( - branchFiles[branchName] && - !branchFiles[branchName].includes(filePath) - ) { - return null; - } - } - try { - const url = `projects/${config.repository}/repository/files/${urlEscape( - filePath - )}?ref=${branchName || config.baseBranch}`; - const res = await get(url); - return Buffer.from(res.body.content, 'base64').toString(); - } catch (error) { - if (error.statusCode === 404) { - // If file not found, then return null JSON - return null; - } - // Propagate if it's any other error - throw error; - } - } - - // GET /projects/:id/repository/commits - async function getCommitMessages() { - logger.debug('getCommitMessages'); - const res = await get(`projects/${config.repository}/repository/commits`); - return res.body.map(commit => commit.title); - } - - function getBranchDetails(branchName) { - const url = `projects/${ - config.repository - }/repository/branches/${urlEscape(branchName)}`; - return get(url); - } - - async function getBaseCommitSHA() { - if (!baseCommitSHA) { - const branchDetails = await getBranchDetails(config.baseBranch); - baseCommitSHA = branchDetails.body.commit.id; - } - return baseCommitSHA; - } - - async function mergeBranch(branchName) { - logger.debug(`mergeBranch(${branchName}`); - const branchURI = encodeURIComponent(branchName); - try { - await get.post( - `projects/${ - config.repository - }/repository/commits/${branchURI}/cherry_pick?branch=${ - config.baseBranch - }` - ); - } catch (err) { - logger.info({ err }, `Error pushing branch merge for ${branchName}`); - throw new Error('Branch automerge failed'); - } - // Update base commit - baseCommitSHA = null; - // Delete branch - await deleteBranch(branchName); - } - - async function getBranchCommit(branchName) { - const branchUrl = `projects/${ - config.repository - }/repository/branches/${urlEscape(branchName)}`; - try { - const branch = (await get(branchUrl)).body; - if (branch && branch.commit) { - return branch.commit.id; - } - } catch (err) { - // istanbul ignore next - logger.error({ err }, `getBranchCommit error`); - } - // istanbul ignore next - return null; - } - - async function getBranchLastCommitTime(branchName) { - try { - const res = await get( - `projects/${ - config.repository - }/repository/commits?ref_name=${urlEscape(branchName)}` - ); - return new Date(res.body[0].committed_date); - } catch (err) { - logger.error({ err }, `getBranchLastCommitTime error`); - return new Date(); - } - } - - async function setBaseBranch(branchName) { - if (branchName) { - logger.debug(`Setting baseBranch to ${branchName}`); - config.baseBranch = branchName; - branchFiles = {}; - await getFileList(branchName); - } - } - } -} - -module.exports = Storage; diff --git a/test/platform/gitlab/__snapshots__/index.spec.js.snap b/test/platform/gitlab/__snapshots__/index.spec.js.snap index b9fdacb00317d32f883cff159b211c5b521d894e..1e5394243db661a824a5df103de5b62c37f1b76c 100644 --- a/test/platform/gitlab/__snapshots__/index.spec.js.snap +++ b/test/platform/gitlab/__snapshots__/index.spec.js.snap @@ -16,57 +16,7 @@ Array [ ] `; -exports[`platform/gitlab commitFilesToBranch(branchName, files, message, parentBranch) creates file 1`] = ` -Array [ - Array [ - "projects/undefined/repository/commits", - Object { - "body": Object { - "actions": Array [ - Object { - "action": "create", - "content": "c29tZSBuZXctY29udGVudHM=", - "encoding": "base64", - "file_path": "some-new-file", - }, - ], - "branch": "renovate/something", - "commit_message": "Create something", - "start_branch": undefined, - }, - }, - ], -] -`; - -exports[`platform/gitlab commitFilesToBranch(branchName, files, message, parentBranch) updates multiple files 1`] = ` -Array [ - Array [ - "projects/undefined/repository/commits", - Object { - "body": Object { - "actions": Array [ - Object { - "action": "update", - "content": "dXBkYXRlZCBjb250ZW50", - "encoding": "base64", - "file_path": "some-existing-file", - }, - Object { - "action": "update", - "content": "b3RoZXIgdXBkYXRlZCBjb250ZW50", - "encoding": "base64", - "file_path": "some-other-existing-file", - }, - ], - "branch": "renovate/something", - "commit_message": "Update something", - "start_branch": undefined, - }, - }, - ], -] -`; +exports[`platform/gitlab commitFilesToBranch() sends to gitFs 1`] = `Array []`; exports[`platform/gitlab createPr(branchName, title, body) returns the PR 1`] = ` Object { @@ -169,15 +119,6 @@ content", ] `; -exports[`platform/gitlab getAllRenovateBranches() should return all renovate branches 1`] = ` -Array [ - "renovate/a", - "renovate/b", -] -`; - -exports[`platform/gitlab getBranchLastCommitTime should return a Date 1`] = `2012-09-20T08:50:22.000Z`; - exports[`platform/gitlab getBranchPr(branchName) should return the PR object 1`] = ` Object { "additions": 1, @@ -186,7 +127,6 @@ Object { }, "body": undefined, "branchName": "some-branch", - "canMerge": true, "canRebase": false, "commits": 1, "deletions": 1, @@ -199,30 +139,6 @@ Object { } `; -exports[`platform/gitlab getCommitMessages() returns commits messages 1`] = ` -Array [ - "foo", - "bar", -] -`; - -exports[`platform/gitlab getFile(filePath, branchName) gets the file 1`] = `"~�"`; - -exports[`platform/gitlab getFile(filePath, branchName) throws error for non-404 1`] = ` -Object { - "statusCode": 403, -} -`; - -exports[`platform/gitlab getFileList should return the files matching the fileName 1`] = ` -Array [ - "package.json", - "some-dir/package.json.some-thing-else", - "src/app/package.json", - "src/otherapp/package.json", -] -`; - exports[`platform/gitlab getPr(prNo) returns the PR 1`] = ` Object { "body": "a merge request", @@ -262,6 +178,24 @@ Object { } `; +exports[`platform/gitlab getPr(prNo) returns the mergeable PR 1`] = ` +Object { + "body": "a merge request", + "branchName": "some-branch", + "canMerge": true, + "canRebase": false, + "description": "a merge request", + "displayNumber": "Merge Request #12345", + "diverged_commits_count": 5, + "id": 1, + "iid": 12345, + "isStale": true, + "number": 12345, + "source_branch": "some-branch", + "state": "open", +} +`; + exports[`platform/gitlab getPrBody(input) returns updated pr body 1`] = ` "https://github.com/foo/bar/issues/5 plus also [a link](https://github.com/foo/bar/issues/5 @@ -326,18 +260,6 @@ Array [ Array [ "user", ], - Array [ - "projects/some%2Frepo%2Fproject/merge_requests?per_page=100", - Object { - "paginate": true, - }, - ], - Array [ - "projects/some%2Frepo%2Fproject/repository/tree?ref=undefined&per_page=100&recursive=true", - Object { - "paginate": true, - }, - ], ] `; @@ -349,18 +271,6 @@ Array [ Array [ "user", ], - Array [ - "projects/some%2Frepo/merge_requests?per_page=100", - Object { - "paginate": true, - }, - ], - Array [ - "projects/some%2Frepo/repository/tree?ref=undefined&per_page=100&recursive=true", - Object { - "paginate": true, - }, - ], ] `; @@ -374,18 +284,6 @@ Array [ Array [ "user", ], - Array [ - "projects/some%2Frepo/merge_requests?per_page=100", - Object { - "paginate": true, - }, - ], - Array [ - "projects/some%2Frepo/repository/tree?ref=undefined&per_page=100&recursive=true", - Object { - "paginate": true, - }, - ], ] `; @@ -399,18 +297,6 @@ Array [ Array [ "user", ], - Array [ - "projects/some%2Frepo/merge_requests?per_page=100", - Object { - "paginate": true, - }, - ], - Array [ - "projects/some%2Frepo/repository/tree?ref=undefined&per_page=100&recursive=true", - Object { - "paginate": true, - }, - ], ] `; @@ -424,58 +310,18 @@ Array [ Array [ "user", ], - Array [ - "projects/some%2Frepo/merge_requests?per_page=100", - Object { - "paginate": true, - }, - ], - Array [ - "projects/some%2Frepo/repository/tree?ref=undefined&per_page=100&recursive=true", - Object { - "paginate": true, - }, - ], ] `; exports[`platform/gitlab initRepo should initialise the config for the repo - 3 2`] = `Object {}`; -exports[`platform/gitlab mergeBranch(branchName) should perform a branch merge 1`] = ` -Array [ - Array [ - "projects/some%2Frepo/repository/commits/thebranchname/cherry_pick?branch=undefined", - ], -] -`; - -exports[`platform/gitlab mergeBranch(branchName) should perform a branch merge 2`] = ` -Array [ - Array [ - "projects/some%2Frepo/repository/branches/thebranchname", - ], -] -`; - -exports[`platform/gitlab mergeBranch(branchName) should throw if branch merge throws 1`] = `[Error: Branch automerge failed]`; - -exports[`platform/gitlab mergeBranch(branchName) should throw if branch merge throws 2`] = ` +exports[`platform/gitlab setBaseBranch(branchName) sets the base branch 1`] = ` Array [ Array [ - "projects/some%2Frepo/repository/commits/thebranchname/cherry_pick?branch=undefined", + "projects/some%2Frepo", ], -] -`; - -exports[`platform/gitlab mergeBranch(branchName) should throw if branch merge throws 3`] = `Array []`; - -exports[`platform/gitlab setBaseBranch(branchName) sets the base branch 1`] = ` -Array [ Array [ - "projects/undefined/repository/tree?ref=some-branch&per_page=100&recursive=true", - Object { - "paginate": true, - }, + "user", ], ] `; diff --git a/test/platform/gitlab/__snapshots__/storage.spec.js.snap b/test/platform/gitlab/__snapshots__/storage.spec.js.snap deleted file mode 100644 index fcc33d87abfca323be31c90b548690e6c992a28a..0000000000000000000000000000000000000000 --- a/test/platform/gitlab/__snapshots__/storage.spec.js.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`platform/gitlab/storage createBranch() creates the branch 1`] = ` -Array [ - Array [ - "projects/undefined/repository/branches?branch=renovate%2Fsome-branch&ref=commit", - ], -] -`; diff --git a/test/platform/gitlab/index.spec.js b/test/platform/gitlab/index.spec.js index b800f76898f49ec1819688138fd77f9389bf2a99..4e83aeab6cb088ffdc4bf19277ea293ecde37be7 100644 --- a/test/platform/gitlab/index.spec.js +++ b/test/platform/gitlab/index.spec.js @@ -3,6 +3,7 @@ const hostRules = require('../../../lib/util/host-rules'); describe('platform/gitlab', () => { let gitlab; let get; + let GitStorage; beforeEach(() => { // clean up hostRules hostRules.clear(); @@ -12,6 +13,31 @@ describe('platform/gitlab', () => { jest.mock('../../../lib/platform/gitlab/gl-got-wrapper'); gitlab = require('../../../lib/platform/gitlab'); get = require('../../../lib/platform/gitlab/gl-got-wrapper'); + jest.mock('../../../lib/platform/git/storage'); + GitStorage = require('../../../lib/platform/git/storage'); + GitStorage.mockImplementation(() => ({ + initRepo: jest.fn(), + cleanRepo: jest.fn(), + getFileList: jest.fn(), + branchExists: jest.fn(() => true), + isBranchStale: jest.fn(() => false), + setBaseBranch: jest.fn(), + getBranchLastCommitTime: jest.fn(), + getAllRenovateBranches: jest.fn(), + getCommitMessages: jest.fn(), + getFile: jest.fn(), + commitFilesToBranch: jest.fn(), + mergeBranch: jest.fn(), + deleteBranch: jest.fn(), + getRepoStatus: jest.fn(), + getBranchCommit: jest.fn( + () => '0d9c7726c3d628b7e28af234595cfd20febdbf8e' + ), + })); + }); + + afterEach(() => { + gitlab.cleanRepo(); }); describe('getRepos', () => { @@ -55,7 +81,8 @@ describe('platform/gitlab', () => { }); describe('getRepoStatus()', () => { it('exists', async () => { - expect(await gitlab.getRepoStatus()).toEqual({}); + await initRepo(); + await gitlab.getRepoStatus(); }); }); describe('cleanRepo()', () => { @@ -86,7 +113,14 @@ describe('platform/gitlab', () => { }, ], }); - return gitlab.initRepo(...args); + if (args.length) { + return gitlab.initRepo(...args); + } + return gitlab.initRepo({ + endpoint: 'https://gitlab.com', + repository: 'some/repo', + token: 'token', + }); } describe('initRepo', () => { @@ -151,153 +185,47 @@ describe('platform/gitlab', () => { }); describe('setBaseBranch(branchName)', () => { it('sets the base branch', async () => { + await initRepo(); await gitlab.setBaseBranch('some-branch'); expect(get.mock.calls).toMatchSnapshot(); }); }); - describe('getFileList', () => { - it('returns empty array if error', async () => { - get.mockImplementationOnce(() => { - throw new Error('some error'); - }); - const files = await gitlab.getFileList(); - expect(files).toEqual([]); - }); - it('warns if truncated result', async () => { - get.mockImplementationOnce(() => ({ - body: [], - })); - const files = await gitlab.getFileList(); - expect(files).toHaveLength(0); - }); - it('caches the result', async () => { - get.mockImplementationOnce(() => ({ - body: [], - })); - let files = await gitlab.getFileList(); - expect(files).toHaveLength(0); - files = await gitlab.getFileList(); - expect(files).toHaveLength(0); - }); - it('should return the files matching the fileName', async () => { - get.mockImplementationOnce(() => ({ - body: [ - { type: 'blob', path: 'symlinks/package.json', mode: '120000' }, - { type: 'blob', path: 'package.json' }, - { - type: 'blob', - path: 'some-dir/package.json.some-thing-else', - }, - { type: 'blob', path: 'src/app/package.json' }, - { type: 'blob', path: 'src/otherapp/package.json' }, - ], - })); - const files = await gitlab.getFileList(); - expect(files).toMatchSnapshot(); + describe('getFileList()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.getFileList(); }); }); - describe('branchExists(branchName)', () => { - it('should return true if 200 OK', async () => { - get.mockImplementationOnce(() => ({ statusCode: 200 })); - const branchExists = await gitlab.branchExists('foo'); - expect(branchExists).toBe(true); - }); - it('should return false if not 200 OK', async () => { - get.mockImplementationOnce(() => ({ statusCode: 500 })); - const branchExists = await gitlab.branchExists('foo'); - expect(branchExists).toBe(false); - }); - it('should return false if 404 error received', async () => { - get.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); - const branchExists = await gitlab.branchExists('foo'); - expect(branchExists).toBe(false); - }); - it('should return error if non-404 error thrown', async () => { - get.mockImplementationOnce(() => - Promise.reject({ - statusCode: 500, - }) - ); - await expect(gitlab.branchExists('foo')).rejects.toEqual({ - statusCode: 500, + describe('branchExists()', () => { + describe('getFileList()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.branchExists(); }); }); }); describe('getAllRenovateBranches()', () => { - it('should return all renovate branches', async () => { - const search = 'renovate/'; - get.mockImplementationOnce(path => { - expect(path).toBe( - 'projects/undefined/repository/branches?search=^renovate%2F' - ); + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.getAllRenovateBranches(); + }); + }); - return { - body: [ - { - name: 'renovate/a', - }, - { - name: 'renovate/b', - }, - ], - }; - }); - const res = await gitlab.getAllRenovateBranches(search); - expect(res).toMatchSnapshot(); + describe('getBranchLastCommitTime()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.getBranchLastCommitTime(); }); }); describe('isBranchStale()', () => { - it('should return false if same SHA as master', async () => { - // getBranchDetails - same as master - get.mockImplementationOnce(() => ({ - body: { - commit: { - parent_ids: ['1234'], - }, - }, - })); - // getBranchDetails - master - get.mockImplementationOnce(() => ({ - body: { - commit: { - id: '1234', - }, - }, - })); - expect(await gitlab.isBranchStale('thebranchname')).toBe(false); - }); - it('should return true if SHA different from master', async () => { - // getBranchDetails - different from master - get.mockImplementationOnce(() => ({ - body: { - commit: { - parent_ids: ['12345678'], - }, - }, - })); - // getBranchDetails - master - get.mockImplementationOnce(() => ({ - body: { - commit: { - id: '1234', - }, - }, - })); - expect(await gitlab.isBranchStale('thebranchname')).toBe(true); + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.isBranchStale(); }); }); describe('getBranchPr(branchName)', () => { - it('should return null if branch does not exist', async () => { - get.mockReturnValueOnce({ statusCode: 500 }); // branchExists - const pr = await gitlab.getBranchPr('somebranch'); - expect(pr).toBeNull(); - }); it('should return null if no PR exists', async () => { - get.mockReturnValueOnce({ statusCode: 200 }); // branchExists + await initRepo(); get.mockReturnValueOnce({ // branchExists body: [], @@ -306,7 +234,7 @@ describe('platform/gitlab', () => { expect(pr).toBeNull(); }); it('should return the PR object', async () => { - get.mockReturnValueOnce({ statusCode: 200 }); // branchExists + await initRepo(); get.mockReturnValueOnce({ body: [{ number: 91, source_branch: 'somebranch' }], }); @@ -349,6 +277,7 @@ describe('platform/gitlab', () => { expect(res).toEqual('failed'); }); it('returns pending if no results', async () => { + await initRepo(); get.mockReturnValueOnce({ body: [], }); @@ -356,6 +285,7 @@ describe('platform/gitlab', () => { expect(res).toEqual('pending'); }); it('returns success if all are success', async () => { + await initRepo(); get.mockReturnValueOnce({ body: [{ status: 'success' }, { status: 'success' }], }); @@ -363,6 +293,7 @@ describe('platform/gitlab', () => { expect(res).toEqual('success'); }); it('returns success if optional jobs fail', async () => { + await initRepo(); get.mockReturnValueOnce({ body: [ { status: 'success' }, @@ -373,6 +304,7 @@ describe('platform/gitlab', () => { expect(res).toEqual('success'); }); it('returns failure if any mandatory jobs fails', async () => { + await initRepo(); get.mockReturnValueOnce({ body: [ { status: 'success' }, @@ -384,6 +316,7 @@ describe('platform/gitlab', () => { expect(res).toEqual('failure'); }); it('returns custom statuses', async () => { + await initRepo(); get.mockReturnValueOnce({ body: [{ status: 'success' }, { status: 'foo' }], }); @@ -392,15 +325,7 @@ describe('platform/gitlab', () => { }); }); describe('getBranchStatusCheck', () => { - beforeEach(() => { - get.mockReturnValueOnce({ - body: { - commit: { - id: 1, - }, - }, - }); - }); + beforeEach(() => initRepo()); it('returns null if no results', async () => { get.mockReturnValueOnce({ body: [], @@ -438,14 +363,7 @@ describe('platform/gitlab', () => { }); describe('setBranchStatus', () => { it('sets branch status', async () => { - // getBranchCommit - get.mockReturnValueOnce({ - body: { - commit: { - id: 1, - }, - }, - }); + await initRepo(); await gitlab.setBranchStatus( 'some-branch', 'some-context', @@ -456,95 +374,16 @@ describe('platform/gitlab', () => { expect(get.post).toHaveBeenCalledTimes(1); }); }); - describe('mergeBranch(branchName)', () => { - it('should perform a branch merge', async () => { - await initRepo({ - repository: 'some/repo', - token: 'token', - }); - - await gitlab.mergeBranch('thebranchname'); - - // deleteBranch - get.delete.mockImplementationOnce(); - - expect(get.post.mock.calls).toMatchSnapshot(); - expect(get.delete.mock.calls).toMatchSnapshot(); - }); - it('should throw if branch merge throws', async () => { - await initRepo({ - repository: 'some/repo', - token: 'token', - }); - get.post.mockImplementationOnce(() => { - throw new Error('branch-push failed'); - }); - - await expect( - gitlab.mergeBranch('thebranchname') - ).rejects.toMatchSnapshot(); - - // deleteBranch - get.delete.mockImplementationOnce(); - - expect(get.post.mock.calls).toMatchSnapshot(); - expect(get.delete.mock.calls).toMatchSnapshot(); + describe('mergeBranch()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.mergeBranch('branch'); }); }); - describe('deleteBranch(branchName)', () => { - it('should send delete', async () => { - get.delete = jest.fn(); - await gitlab.deleteBranch('some-branch'); - expect(get.delete).toHaveBeenCalledTimes(1); - }); - it('should close PR', async () => { - get.delete = jest.fn(); - get.mockReturnValueOnce({ body: [] }); // getBranchPr - await gitlab.deleteBranch('some-branch', true); - expect(get.delete).toHaveBeenCalledTimes(1); - }); - }); - describe('getBranchLastCommitTime', () => { - it('should return a Date', async () => { - get.mockReturnValueOnce({ - body: [ - { - id: 'ed899a2f4b50b4370feeea94676502b42383c746', - short_id: 'ed899a2f4b5', - title: 'Replace sanitize with escape once', - author_name: 'Dmitriy Zaporozhets', - author_email: 'dzaporozhets@sphereconsultinginc.com', - authored_date: '2012-09-20T11:50:22+03:00', - committer_name: 'Administrator', - committer_email: 'admin@example.com', - committed_date: '2012-09-20T11:50:22+03:00', - created_at: '2012-09-20T11:50:22+03:00', - message: 'Replace sanitize with escape once', - parent_ids: ['6104942438c14ec7bd21c6cd5bd995272b3faff6'], - }, - { - id: '6104942438c14ec7bd21c6cd5bd995272b3faff6', - short_id: '6104942438c', - title: 'Sanitize for network graph', - author_name: 'randx', - author_email: 'dmitriy.zaporozhets@gmail.com', - committer_name: 'Dmitriy', - committer_email: 'dmitriy.zaporozhets@gmail.com', - created_at: '2012-09-20T09:06:12+03:00', - message: 'Sanitize for network graph', - parent_ids: ['ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba'], - }, - ], - }); - const res = await gitlab.getBranchLastCommitTime('some-branch'); - expect(res).toMatchSnapshot(); - }); - it('handles error', async () => { - get.mockReturnValueOnce({ - body: [], - }); - const res = await gitlab.getBranchLastCommitTime('some-branch'); - expect(res).toBeDefined(); + describe('deleteBranch()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.deleteBranch('branch', true); }); }); describe('findIssue()', () => { @@ -828,6 +667,22 @@ describe('platform/gitlab', () => { const pr = await gitlab.getPr(12345); expect(pr).toMatchSnapshot(); }); + it('returns the mergeable PR', async () => { + await initRepo(); + get.mockReturnValueOnce({ + body: { + id: 1, + iid: 12345, + description: 'a merge request', + state: 'open', + diverged_commits_count: 5, + source_branch: 'some-branch', + }, + }); + get.mockReturnValueOnce({ body: [{ status: 'success' }] }); // get commit statuses + const pr = await gitlab.getPr(12345); + expect(pr).toMatchSnapshot(); + }); it('returns the PR with nonexisting branch', async () => { get.mockImplementationOnce(() => ({ body: { @@ -897,114 +752,24 @@ These updates have all been created already. Click a checkbox below to force a r expect(gitlab.getPrBody(prBody)).toMatchSnapshot(); }); }); - describe('getFile(filePath, branchName)', () => { - beforeEach(async () => { - get.mockReturnValueOnce({ body: [{ type: 'blob', path: 'some-path' }] }); - await gitlab.getFileList(); - }); - it('gets the file', async () => { - get.mockReturnValueOnce({ - body: { - content: 'foo', - }, - }); - const res = await gitlab.getFile('some-path'); - expect(res).toMatchSnapshot(); - }); - it('short cuts 404', async () => { - const res = await gitlab.getFile('some-missing-path'); - expect(res).toBeNull(); - }); - it('returns null for 404', async () => { - get.mockImplementationOnce(() => Promise.reject({ statusCode: 404 })); - const res = await gitlab.getFile('some-path', 'some-branch'); - expect(res).toBeNull(); - }); - it('throws error for non-404', async () => { - get.mockImplementationOnce(() => Promise.reject({ statusCode: 403 })); - - await expect( - gitlab.getFile('some-path', 'some-branch') - ).rejects.toMatchSnapshot(); + describe('getFile()', () => { + it('sends to gitFs', async () => { + await initRepo(); + await gitlab.getFile(); }); }); - describe('commitFilesToBranch(branchName, files, message, parentBranch)', () => { - it('creates file', async () => { - get.mockImplementationOnce(() => Promise.reject({ statusCode: 404 })); // file exists - get.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); // branch exists - get.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); // branch exists - const file = { - name: 'some-new-file', - contents: 'some new-contents', - }; - await gitlab.commitFilesToBranch( - 'renovate/something', - [file], - 'Create something' - ); - expect(get.post.mock.calls).toMatchSnapshot(); - expect(get.post).toHaveBeenCalledTimes(1); - }); - it('updates multiple files', async () => { - // Two files exist - get.mockReturnValueOnce({ - body: { - content: 'foo', - }, - }); - get.mockReturnValueOnce({ - body: { - content: 'foo', - }, - }); - // branch exists - get.mockImplementationOnce(() => ({ statusCode: 200 })); - get.mockImplementationOnce(() => - Promise.reject({ - statusCode: 404, - }) - ); // branch exists - const files = [ - { - name: 'some-existing-file', - contents: 'updated content', - }, - { - name: 'some-other-existing-file', - contents: 'other updated content', - }, - ]; - await gitlab.commitFilesToBranch( - 'renovate/something', - files, - 'Update something' - ); - expect(get.post.mock.calls).toMatchSnapshot(); - expect(get.post).toHaveBeenCalledTimes(1); + describe('commitFilesToBranch()', () => { + it('sends to gitFs', async () => { + expect.assertions(1); + await initRepo(); + await gitlab.commitFilesToBranch('some-branch', [{}]); + expect(get.get.mock.calls).toMatchSnapshot(); }); }); describe('getCommitMessages()', () => { - it('returns commits messages', async () => { - get.mockReturnValueOnce({ - body: [ - { - title: 'foo', - }, - { - title: 'bar', - }, - ], - }); - const res = await gitlab.getCommitMessages(); - expect(res).toMatchSnapshot(); + it('passes to gitFs', async () => { + await initRepo(); + await gitlab.getCommitMessages(); }); }); describe('getVulnerabilityAlerts()', () => { diff --git a/test/platform/gitlab/storage.spec.js b/test/platform/gitlab/storage.spec.js deleted file mode 100644 index 38571820cf3f96b318c9e0ba00bdc5c0fe772b82..0000000000000000000000000000000000000000 --- a/test/platform/gitlab/storage.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -describe('platform/gitlab/storage', () => { - jest.mock('../../../lib/platform/gitlab/gl-got-wrapper'); - const GitlabStorage = require('../../../lib/platform/gitlab/storage'); - const GitStorage = require('../../../lib/platform/git/storage'); - const get = require('../../../lib/platform/gitlab/gl-got-wrapper'); - it('has same API for git storage', () => { - const gitlabMethods = Object.keys(new GitlabStorage()).sort(); - const gitMethods = Object.keys(new GitStorage()).sort(); - expect(gitlabMethods).toMatchObject(gitMethods); - }); - it('getRepoStatus exists', async () => { - expect((await new GitlabStorage()).getRepoStatus()).toEqual({}); - }); - describe('createBranch()', () => { - it('creates the branch', async () => { - get.post.mockReturnValue({}); - const storage = new GitlabStorage(); - await storage.createBranch('renovate/some-branch', 'commit'); - expect(get.post.mock.calls).toMatchSnapshot(); - }); - }); -}); diff --git a/test/workers/repository/init/apis.spec.js b/test/workers/repository/init/apis.spec.js index 87a968bfb19782c3d76f537f61b06e4c781f9a4a..b8820793765c2e92debf1e8420cf6a091da8b3cc 100644 --- a/test/workers/repository/init/apis.spec.js +++ b/test/workers/repository/init/apis.spec.js @@ -1,4 +1,3 @@ -const glGot = require('gl-got'); const { initApis } = require('../../../../lib/workers/repository/init/apis'); jest.mock('../../../../lib/platform/github'); @@ -15,14 +14,5 @@ describe('workers/repository/init/apis', () => { it('runs', async () => { await initApis(config); }); - it('runs gitlab', async () => { - config.platform = 'gitlab'; - config.repository = 'some/name'; - glGot.mockReturnValueOnce({ body: {} }); - glGot.mockReturnValueOnce({ body: {} }); - glGot.mockReturnValueOnce({ body: [] }); - glGot.mockReturnValueOnce({ body: [] }); - await initApis(config); - }); }); });