diff --git a/lib/workers/repository.js b/lib/workers/repository.js
index 7329ee5cfb42c33acbf4013d1d1eb9550dcf69b8..b4e80398df2cc9cf0b4acc13af820f7e6cc52ee5 100644
--- a/lib/workers/repository.js
+++ b/lib/workers/repository.js
@@ -17,6 +17,11 @@ module.exports = {
   processRepo,
   processUpgrades,
   removeStandaloneBranches,
+  mergeRenovateJson,
+  checkIfOnboarded,
+  setNpmrc,
+  detectPackageFiles,
+  getAllRepoUpgrades,
 };
 
 // This will be github or others
@@ -48,17 +53,17 @@ async function processRepo(config) {
       logger
     );
     // Override settings with renovate.json if present
-    await mergeRenovateJson(config);
+    await module.exports.mergeRenovateJson(config);
     // Check that the repository is onboarded
-    const isOnboarded = await checkIfOnboarded(config);
+    const isOnboarded = await module.exports.checkIfOnboarded(config);
     if (isOnboarded === false) {
       return;
     }
     // Check for presence of .npmrc in repository
-    await setNpmrc(config);
+    await module.exports.setNpmrc(config);
     // Detect package files if none already configured
-    await detectPackageFiles(config);
-    const upgrades = await getAllRepoUpgrades(config);
+    await module.exports.detectPackageFiles(config);
+    const upgrades = await module.exports.getAllRepoUpgrades(config);
     await module.exports.processUpgrades(upgrades);
   } catch (error) {
     throw error;
diff --git a/test/api/__snapshots__/gitlab.spec.js.snap b/test/api/__snapshots__/gitlab.spec.js.snap
new file mode 100644
index 0000000000000000000000000000000000000000..d54a7bf4216db663f641f4d60a0035a1c1fa3e6e
--- /dev/null
+++ b/test/api/__snapshots__/gitlab.spec.js.snap
@@ -0,0 +1,118 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`api/gitlab getRepos should return an array of repos 1`] = `
+Array [
+  Array [
+    "projects",
+  ],
+]
+`;
+
+exports[`api/gitlab getRepos should return an array of repos 2`] = `
+Array [
+  "a/b",
+  "c/d",
+]
+`;
+
+exports[`api/gitlab getRepos should support a custom endpoint 1`] = `
+Array [
+  Array [
+    "projects",
+  ],
+]
+`;
+
+exports[`api/gitlab getRepos should support a custom endpoint 2`] = `
+Array [
+  "a/b",
+  "c/d",
+]
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 0 1`] = `
+Array [
+  Array [
+    "projects/owned",
+  ],
+  Array [
+    "projects/some%2Frepo",
+  ],
+  Array [
+    "user",
+  ],
+]
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 0 2`] = `
+Object {
+  "apiVersion": "v3",
+  "defaultBranch": "master",
+  "email": "a@b.com",
+  "repoName": "some%2Frepo",
+}
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 1 1`] = `
+Array [
+  Array [
+    "projects/owned",
+  ],
+  Array [
+    "projects/some%2Frepo",
+  ],
+  Array [
+    "user",
+  ],
+]
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 1 2`] = `
+Object {
+  "apiVersion": "v3",
+  "defaultBranch": "master",
+  "email": "a@b.com",
+  "repoName": "some%2Frepo",
+}
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 2 1`] = `
+Array [
+  Array [
+    "projects/owned",
+  ],
+  Array [
+    "projects/some%2Frepo",
+  ],
+  Array [
+    "user",
+  ],
+]
+`;
+
+exports[`api/gitlab initRepo should initialise the config for the repo - 2 2`] = `
+Object {
+  "apiVersion": "v3",
+  "defaultBranch": "master",
+  "email": "a@b.com",
+  "repoName": "some%2Frepo",
+}
+`;
+
+exports[`api/gitlab initRepo should use api v4 1`] = `
+Object {
+  "apiVersion": "v4",
+  "defaultBranch": "master",
+  "email": "a@b.com",
+  "repoName": "some%2Frepo",
+}
+`;
+
+exports[`api/gitlab initRepo uses provided logger 1`] = `
+Object {
+  "apiVersion": "v3",
+  "defaultBranch": "master",
+  "email": "a@b.com",
+  "repoName": "some%2Frepo",
+}
+`;
diff --git a/test/api/github.spec.js b/test/api/github.spec.js
index 135892969610fe98821d94f910fbbfea52d99985..8405d9704956bb33c3f91cbd3f3cd2e1f25c8a04 100644
--- a/test/api/github.spec.js
+++ b/test/api/github.spec.js
@@ -1,3 +1,11 @@
+const bunyan = require('bunyan');
+
+const logger = bunyan.createLogger({
+  name: 'test',
+  stream: process.stdout,
+  level: 'fatal',
+});
+
 describe('api/github', () => {
   let github;
   let ghGot;
@@ -175,6 +183,9 @@ describe('api/github', () => {
         expect(process.env.GITHUB_ENDPOINT).toBe(endpoint);
       });
     });
+    it('uses provided logger', async () => {
+      await initRepo('some/repo', 'some_token', 'an_endpoint', logger);
+    });
     it('should throw an error if no token is provided', async () => {
       let err;
       try {
diff --git a/test/api/gitlab.spec.js b/test/api/gitlab.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..a02d24eccd8d2345f223b69ba3c3ab5266388250
--- /dev/null
+++ b/test/api/gitlab.spec.js
@@ -0,0 +1,207 @@
+const bunyan = require('bunyan');
+
+const logger = bunyan.createLogger({
+  name: 'test',
+  stream: process.stdout,
+  level: 'fatal',
+});
+
+describe('api/gitlab', () => {
+  let gitlab;
+  let glGot;
+  beforeEach(() => {
+    // clean up env
+    delete process.env.GITLAB_TOKEN;
+    delete process.env.GITLAB_ENDPOINT;
+
+    // reset module
+    jest.resetModules();
+    jest.mock('gl-got');
+    gitlab = require('../../lib/api/gitlab');
+    glGot = require('gl-got');
+  });
+
+  describe('getRepos', () => {
+    async function getRepos(...args) {
+      // repo info
+      glGot.mockImplementationOnce(() => ({
+        body: [
+          {
+            path_with_namespace: 'a/b',
+          },
+          {
+            path_with_namespace: 'c/d',
+          },
+        ],
+      }));
+      return gitlab.getRepos(...args);
+    }
+    it('should throw an error if no token is provided', async () => {
+      let err;
+      try {
+        await gitlab.getRepos();
+      } catch (e) {
+        err = e;
+      }
+      expect(err.message).toBe('No token found for getRepos');
+    });
+    it('should throw an error if it receives an error', async () => {
+      glGot.mockImplementation(() => {
+        throw new Error('getRepos error');
+      });
+      let err;
+      try {
+        await gitlab.getRepos('sometoken');
+      } catch (e) {
+        err = e;
+      }
+      expect(err.message).toBe('getRepos error');
+    });
+    it('should return an array of repos', async () => {
+      const repos = await getRepos('sometoken');
+      expect(glGot.mock.calls).toMatchSnapshot();
+      expect(repos).toMatchSnapshot();
+    });
+    it('should support a custom endpoint', async () => {
+      const repos = await getRepos('sometoken', 'someendpoint');
+      expect(glGot.mock.calls).toMatchSnapshot();
+      expect(repos).toMatchSnapshot();
+    });
+  });
+
+  async function initRepo(...args) {
+    // projects/owned
+    glGot.mockImplementationOnce();
+    // projects/${config.repoName
+    glGot.mockImplementationOnce(() => ({
+      body: {
+        default_branch: 'master',
+      },
+    }));
+    // user
+    glGot.mockImplementationOnce(() => ({
+      body: {
+        email: 'a@b.com',
+      },
+    }));
+    return gitlab.initRepo(...args);
+  }
+
+  describe('initRepo', () => {
+    [
+      [undefined, ['mytoken'], 'mytoken', undefined],
+      [
+        undefined,
+        ['mytoken', 'https://my.custom.endpoint/'],
+        'mytoken',
+        'https://my.custom.endpoint/',
+      ],
+      ['myenvtoken', [], 'myenvtoken', undefined],
+    ].forEach(([envToken, args, token, endpoint], i) => {
+      it(`should initialise the config for the repo - ${i}`, async () => {
+        if (envToken !== undefined) {
+          process.env.GITLAB_TOKEN = envToken;
+        }
+        const config = await initRepo('some/repo', ...args);
+        expect(glGot.mock.calls).toMatchSnapshot();
+        expect(config).toMatchSnapshot();
+        expect(process.env.GITLAB_TOKEN).toBe(token);
+        expect(process.env.GITLAB_ENDPOINT).toBe(endpoint);
+      });
+    });
+    it('uses provided logger', async () => {
+      const config = await initRepo(
+        'some/repo',
+        'some_token',
+        'an_endpoint',
+        logger
+      );
+      expect(config).toMatchSnapshot();
+    });
+    it('should throw an error if no token is provided', async () => {
+      let err;
+      try {
+        await gitlab.initRepo('some/repo');
+      } catch (e) {
+        err = e;
+      }
+      expect(err.message).toBe(
+        'No token found for GitLab repository some/repo'
+      );
+    });
+    it('should throw an error if receiving an error', async () => {
+      glGot.mockImplementation(() => {
+        throw new Error('always error');
+      });
+      let err;
+      try {
+        await gitlab.initRepo('some/repo', 'sometoken');
+      } catch (e) {
+        err = e;
+      }
+      expect(err.message).toBe('always error');
+    });
+    it('should use api v4', async () => {
+      // projects/owned
+      glGot.mockImplementationOnce(() => {
+        throw new Error('any error');
+      });
+      // projects/${config.repoName
+      glGot.mockImplementationOnce(() => ({
+        body: {
+          default_branch: 'master',
+        },
+      }));
+      // user
+      glGot.mockImplementationOnce(() => ({
+        body: {
+          email: 'a@b.com',
+        },
+      }));
+      const config = await initRepo('some/repo', 'some_token');
+      expect(config).toMatchSnapshot();
+    });
+  });
+  describe('findFilePaths(fileName)', () => {
+    it('should return the fileName', async () => {
+      await initRepo('some/repo', 'token');
+      const files = await gitlab.findFilePaths('package.json');
+      expect(files).toEqual(['package.json']);
+    });
+  });
+  describe('branchExists(branchName)', () => {
+    it('should return true if 200 OK', async () => {
+      glGot.mockImplementationOnce(() => ({ statusCode: 200 }));
+      const branchExists = await gitlab.branchExists('foo');
+      expect(branchExists).toBe(true);
+    });
+    it('should return false if not 200 OK', async () => {
+      glGot.mockImplementationOnce(() => ({ statusCode: 500 }));
+      const branchExists = await gitlab.branchExists('foo');
+      expect(branchExists).toBe(false);
+    });
+    it('should return false if 404 error received', async () => {
+      glGot.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 () => {
+      glGot.mockImplementationOnce(() =>
+        Promise.reject({
+          statusCode: 500,
+        })
+      );
+      let e;
+      try {
+        await gitlab.branchExists('foo');
+      } catch (err) {
+        e = err;
+      }
+      expect(e.statusCode).toBe(500);
+    });
+  });
+});
diff --git a/test/api/npm.spec.js b/test/api/npm.spec.js
index a3f8e7331ad8ba57f7673f9a53664b3c7ce4975a..0989b12b6dee6a408e8037214f5b5c33d45989c2 100644
--- a/test/api/npm.spec.js
+++ b/test/api/npm.spec.js
@@ -58,4 +58,7 @@ describe('api/npm', () => {
     const call = got.mock.calls[0];
     expect(call).toMatchSnapshot();
   });
+  it('sets .npmrc', () => {
+    npm.setNpmrc('input');
+  });
 });
diff --git a/test/renovate.spec.js b/test/renovate.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..2d93861f8ce3d6bac367c984e80e283d4d1a098f
--- /dev/null
+++ b/test/renovate.spec.js
@@ -0,0 +1,10 @@
+const renovateWorker = require('../lib/workers');
+
+renovateWorker.start = jest.fn();
+
+describe('renovate', () => {
+  it('starts', () => {
+    require('../lib/renovate');
+    expect(renovateWorker.start.mock.calls.length).toBe(1);
+  });
+});
diff --git a/test/workers/branch.spec.js b/test/workers/branch.spec.js
index 20d5ad7e5d60e4e0884854aba8994082f515d873..a740fb4438fdb412dd2ab6b6a83f85b0e61cdc09 100644
--- a/test/workers/branch.spec.js
+++ b/test/workers/branch.spec.js
@@ -333,8 +333,8 @@ describe('workers/branch', () => {
       config.api = {
         checkForClosedPr: jest.fn(),
       };
-      branchWorker.ensureBranch = jest.fn();
-      prWorker.ensurePr = jest.fn();
+      branchWorker.ensureBranch = jest.fn(() => true);
+      prWorker.ensurePr = jest.fn(() => true);
     });
     it('returns immediately if closed PR found', async () => {
       config.api.checkForClosedPr.mockReturnValue(true);
diff --git a/test/workers/pr.spec.js b/test/workers/pr.spec.js
index fb43fbefe7b7e47e4fe3f5f61e940f88c6f0f6ad..deff4e1fbc2f9b9c92175bade0da350020916ca2 100644
--- a/test/workers/pr.spec.js
+++ b/test/workers/pr.spec.js
@@ -223,5 +223,10 @@ describe('workers/pr', () => {
       const pr = await prWorker.ensurePr([config], logger);
       expect(pr).toBe(null);
     });
+    it('handles duplicate upgrades', async () => {
+      config.api.getBranchPr = jest.fn();
+      const pr = await prWorker.ensurePr([config, config], logger);
+      expect(pr).toMatchObject({ displayNumber: 'New Pull Request' });
+    });
   });
 });
diff --git a/test/workers/repository.spec.js b/test/workers/repository.spec.js
index f99ddc6252400d96cf281cbeaa5220df85d971b5..2babfd8c83cba69e071dcb9e472762df563411ea 100644
--- a/test/workers/repository.spec.js
+++ b/test/workers/repository.spec.js
@@ -4,6 +4,8 @@ const branchWorker = require('../../lib/workers/branch');
 jest.mock('../../lib/workers/branch');
 jest.mock('../../lib/workers/pr');
 jest.mock('../../lib/api/npm');
+jest.mock('../../lib/api/github');
+jest.mock('../../lib/api/gitlab');
 jest.mock('../../lib/helpers/versions');
 
 describe('repositoryWorker', () => {