diff --git a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap b/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap
deleted file mode 100644
index c08c86e20b0000dbe269d7e3268a7f0f3dd7efcd..0000000000000000000000000000000000000000
--- a/lib/modules/platform/gitea/__snapshots__/index.spec.ts.snap
+++ /dev/null
@@ -1,235 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`modules/platform/gitea/index createPr should use base branch by default 1`] = `
-{
-  "bodyStruct": {
-    "hash": "9d586a6aedc4e7cb205276933c9e474cd3c2b341d3340458c31eb750795f197d",
-  },
-  "cannotMergeReason": undefined,
-  "createdAt": "2014-04-01T05:14:20Z",
-  "hasAssignees": false,
-  "isDraft": false,
-  "number": 42,
-  "sha": "0d9c7726c3d628b7e28af234595cfd20febdbf8e",
-  "sourceBranch": "pr-branch",
-  "sourceRepo": "some/repo",
-  "state": "open",
-  "targetBranch": "devel",
-  "title": "pr-title",
-}
-`;
-
-exports[`modules/platform/gitea/index createPr should use default branch if requested 1`] = `
-{
-  "bodyStruct": {
-    "hash": "9d586a6aedc4e7cb205276933c9e474cd3c2b341d3340458c31eb750795f197d",
-  },
-  "cannotMergeReason": undefined,
-  "createdAt": "2014-04-01T05:14:20Z",
-  "hasAssignees": false,
-  "isDraft": false,
-  "number": 42,
-  "sha": "0d9c7726c3d628b7e28af234595cfd20febdbf8e",
-  "sourceBranch": "pr-branch",
-  "sourceRepo": "some/repo",
-  "state": "open",
-  "targetBranch": "master",
-  "title": "pr-title",
-}
-`;
-
-exports[`modules/platform/gitea/index getPr should fallback to direct fetching if cache fails 1`] = `
-{
-  "bodyStruct": {
-    "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
-  },
-  "cannotMergeReason": "pr.mergeable="false"",
-  "createdAt": "2015-03-22T20:36:16Z",
-  "hasAssignees": false,
-  "isDraft": false,
-  "number": 1,
-  "sha": "some-head-sha",
-  "sourceBranch": "some-head-branch",
-  "sourceRepo": "some/repo",
-  "state": "open",
-  "targetBranch": "some-base-branch",
-  "title": "Some PR",
-}
-`;
-
-exports[`modules/platform/gitea/index getPr should return enriched pull request which exists if open 1`] = `
-{
-  "bodyStruct": {
-    "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
-  },
-  "cannotMergeReason": undefined,
-  "createdAt": "2015-03-22T20:36:16Z",
-  "hasAssignees": false,
-  "isDraft": false,
-  "number": 1,
-  "sha": "some-head-sha",
-  "sourceBranch": "some-head-branch",
-  "sourceRepo": "some/repo",
-  "state": "open",
-  "targetBranch": "some-base-branch",
-  "title": "Some PR",
-}
-`;
-
-exports[`modules/platform/gitea/index getPrList should filter list by creator 1`] = `
-{
-  "endpoint": "https://gitea.com/",
-  "gitAuthor": "Renovate Bot <renovate@example.com>",
-}
-`;
-
-exports[`modules/platform/gitea/index getPrList should filter list by creator 2`] = `
-[
-  {
-    "bodyStruct": {
-      "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2015-03-22T20:36:16Z",
-    "hasAssignees": false,
-    "isDraft": false,
-    "number": 1,
-    "sha": "some-head-sha",
-    "sourceBranch": "some-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "open",
-    "targetBranch": "some-base-branch",
-    "title": "Some PR",
-  },
-  {
-    "bodyStruct": {
-      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2011-08-18T22:30:38Z",
-    "hasAssignees": false,
-    "isDraft": false,
-    "number": 2,
-    "sha": "other-head-sha",
-    "sourceBranch": "other-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "closed",
-    "targetBranch": "other-base-branch",
-    "title": "Other PR",
-  },
-  {
-    "bodyStruct": {
-      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2011-08-18T22:30:39Z",
-    "hasAssignees": false,
-    "isDraft": true,
-    "number": 3,
-    "sha": "draft-head-sha",
-    "sourceBranch": "draft-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "open",
-    "targetBranch": "draft-base-branch",
-    "title": "Draft PR",
-  },
-]
-`;
-
-exports[`modules/platform/gitea/index getPrList should return list of pull requests 1`] = `
-[
-  {
-    "bodyStruct": {
-      "hash": "f41557d6153a316ee747e13de8952c4068de931585c1a18d095d6703254de6af",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2015-03-22T20:36:16Z",
-    "hasAssignees": false,
-    "isDraft": false,
-    "number": 1,
-    "sha": "some-head-sha",
-    "sourceBranch": "some-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "open",
-    "targetBranch": "some-base-branch",
-    "title": "Some PR",
-  },
-  {
-    "bodyStruct": {
-      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2011-08-18T22:30:38Z",
-    "hasAssignees": false,
-    "isDraft": false,
-    "number": 2,
-    "sha": "other-head-sha",
-    "sourceBranch": "other-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "closed",
-    "targetBranch": "other-base-branch",
-    "title": "Other PR",
-  },
-  {
-    "bodyStruct": {
-      "hash": "916e5965a20785df1883ff5dc219508a1070ae1f37ccb64e954526f3ca1d22f4",
-    },
-    "cannotMergeReason": undefined,
-    "createdAt": "2011-08-18T22:30:39Z",
-    "hasAssignees": false,
-    "isDraft": true,
-    "number": 3,
-    "sha": "draft-head-sha",
-    "sourceBranch": "draft-head-branch",
-    "sourceRepo": "some/repo",
-    "state": "open",
-    "targetBranch": "draft-base-branch",
-    "title": "Draft PR",
-  },
-]
-`;
-
-exports[`modules/platform/gitea/index initPlatform() should support custom endpoint 1`] = `
-{
-  "endpoint": "https://gitea.renovatebot.com/",
-  "gitAuthor": "Renovate Bot <renovate@example.com>",
-}
-`;
-
-exports[`modules/platform/gitea/index initPlatform() should support default endpoint 1`] = `
-{
-  "endpoint": "https://gitea.com/",
-  "gitAuthor": "Renovate Bot <renovate@example.com>",
-}
-`;
-
-exports[`modules/platform/gitea/index initPlatform() should use username as author name if full name is missing 1`] = `
-{
-  "endpoint": "https://gitea.com/",
-  "gitAuthor": "renovate <renovate@example.com>",
-}
-`;
-
-exports[`modules/platform/gitea/index initRepo should fall back to merge method "merge" 1`] = `
-{
-  "defaultBranch": "master",
-  "isFork": false,
-  "repoFingerprint": "c48ad9428365701f1a7f4798a410db2401b13267c205e345beb5b469a4a1480b163e1ce663ce483cfe579b2748a807cbeeba2035dc55eca5fe46d60d182510ec",
-}
-`;
-
-exports[`modules/platform/gitea/index initRepo should fall back to merge method "rebase-merge" 1`] = `
-{
-  "defaultBranch": "master",
-  "isFork": false,
-  "repoFingerprint": "c48ad9428365701f1a7f4798a410db2401b13267c205e345beb5b469a4a1480b163e1ce663ce483cfe579b2748a807cbeeba2035dc55eca5fe46d60d182510ec",
-}
-`;
-
-exports[`modules/platform/gitea/index initRepo should fall back to merge method "squash" 1`] = `
-{
-  "defaultBranch": "master",
-  "isFork": false,
-  "repoFingerprint": "c48ad9428365701f1a7f4798a410db2401b13267c205e345beb5b469a4a1480b163e1ce663ce483cfe579b2748a807cbeeba2035dc55eca5fe46d60d182510ec",
-}
-`;
diff --git a/lib/modules/platform/gitea/index.spec.ts b/lib/modules/platform/gitea/index.spec.ts
index d4321b823be8f4a7f7a7b736ac364971024f4931..a7e8280aba10097634e37760133225166e00e4f7 100644
--- a/lib/modules/platform/gitea/index.spec.ts
+++ b/lib/modules/platform/gitea/index.spec.ts
@@ -1,10 +1,5 @@
-import type {
-  BranchStatusConfig,
-  EnsureIssueConfig,
-  Platform,
-  RepoParams,
-  RepoResult,
-} from '..';
+import type { EnsureIssueConfig, Platform, RepoParams } from '..';
+import * as httpMock from '../../../../test/http-mock';
 import { mocked, partial } from '../../../../test/util';
 import {
   CONFIG_GIT_URL_UNAVAILABLE,
@@ -16,27 +11,20 @@ import {
   REPOSITORY_MIRRORED,
 } from '../../../constants/error-messages';
 import type { logger as _logger } from '../../../logger';
-import type { BranchStatus, PrState } from '../../../types';
 import type * as _git from '../../../util/git';
 import type { LongCommitSha } from '../../../util/git/types';
 import { setBaseUrl } from '../../../util/http/gitea';
-import type { PlatformResult } from '../types';
 import type {
-  Branch,
-  CombinedCommitStatus,
   Comment,
   CommitStatus,
   CommitStatusType,
-  CommitUser,
   Issue,
   Label,
   PR,
   Repo,
-  RepoContents,
   User,
 } from './types';
 
-jest.mock('./gitea-helper');
 jest.mock('../../../util/git');
 
 /**
@@ -46,9 +34,8 @@ const GITEA_VERSION = '1.14.0+dev-754-g5d2b7ba63';
 
 describe('modules/platform/gitea/index', () => {
   let gitea: Platform;
-  let helper: jest.Mocked<typeof import('./gitea-helper')>;
   let logger: jest.Mocked<typeof _logger>;
-  let gitvcs: jest.Mocked<typeof _git>;
+  let git: jest.Mocked<typeof _git>;
   let hostRules: typeof import('../../../util/host-rules');
 
   const mockCommitHash =
@@ -123,7 +110,7 @@ describe('modules/platform/gitea/index', () => {
       diff_url: 'https://gitea.renovatebot.com/some/repo/pulls/3.diff',
       created_at: '2011-08-18T22:30:39Z',
       closed_at: '2016-01-09T10:03:22Z',
-      mergeable: true,
+      mergeable: false,
       base: { ref: 'draft-base-branch' },
       head: {
         label: 'draft-head-branch',
@@ -206,11 +193,10 @@ describe('modules/platform/gitea/index', () => {
     jest.resetModules();
 
     gitea = await import('.');
-    helper = jest.requireMock('./gitea-helper');
     logger = mocked(await import('../../../logger')).logger;
-    gitvcs = jest.requireMock('../../../util/git');
-    gitvcs.isBranchBehindBase.mockResolvedValue(false);
-    gitvcs.getBranchCommit.mockReturnValue(mockCommitHash);
+    git = jest.requireMock('../../../util/git');
+    git.isBranchBehindBase.mockResolvedValue(false);
+    git.getBranchCommit.mockReturnValue(mockCommitHash);
     hostRules = await import('../../../util/host-rules');
     hostRules.clear();
 
@@ -220,22 +206,27 @@ describe('modules/platform/gitea/index', () => {
     delete process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER;
   });
 
-  function initFakePlatform(version = GITEA_VERSION): Promise<PlatformResult> {
-    helper.getCurrentUser.mockResolvedValueOnce(mockUser);
-    helper.getVersion.mockResolvedValueOnce(version);
-    return gitea.initPlatform({ token: 'abc' });
+  async function initFakePlatform(
+    scope: httpMock.Scope,
+    version = GITEA_VERSION,
+  ): Promise<void> {
+    scope
+      .get('/user')
+      .reply(200, mockUser)
+      .get('/version')
+      .reply(200, { version });
+    await gitea.initPlatform({ token: 'abc' });
   }
 
-  function initFakeRepo(
+  async function initFakeRepo(
+    scope: httpMock.Scope,
     repo?: Partial<Repo>,
     config?: Partial<RepoParams>,
-  ): Promise<RepoResult> {
-    helper.getRepo.mockResolvedValueOnce({ ...mockRepo, ...repo });
-
-    return gitea.initRepo({
-      repository: mockRepo.full_name,
-      ...config,
-    });
+  ): Promise<void> {
+    const repoResult = { ...mockRepo, ...repo };
+    const repository = repoResult.full_name;
+    scope.get(`/repos/${repository}`).reply(200, repoResult);
+    await gitea.initRepo({ repository, ...config });
   }
 
   describe('initPlatform()', () => {
@@ -244,7 +235,8 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should throw if auth fails', async () => {
-      helper.getCurrentUser.mockRejectedValueOnce(new Error());
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      scope.get('/user').reply(500);
 
       await expect(
         gitea.initPlatform({ token: 'some-token' }),
@@ -252,81 +244,128 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should support default endpoint', async () => {
-      helper.getCurrentUser.mockResolvedValueOnce(mockUser);
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      scope
+        .get('/user')
+        .reply(200, mockUser)
+        .get('/version')
+        .reply(200, { version: GITEA_VERSION });
 
-      expect(
-        await gitea.initPlatform({ token: 'some-token' }),
-      ).toMatchSnapshot();
+      expect(await gitea.initPlatform({ token: 'some-token' })).toEqual({
+        endpoint: 'https://gitea.com/',
+        gitAuthor: 'Renovate Bot <renovate@example.com>',
+      });
     });
 
     it('should support custom endpoint', async () => {
-      helper.getCurrentUser.mockResolvedValueOnce(mockUser);
+      const scope = httpMock.scope('https://gitea.renovatebot.com/api/v1');
+      scope
+        .get('/user')
+        .reply(200, mockUser)
+        .get('/version')
+        .reply(200, { version: GITEA_VERSION });
 
       expect(
         await gitea.initPlatform({
           token: 'some-token',
           endpoint: 'https://gitea.renovatebot.com',
         }),
-      ).toMatchSnapshot();
+      ).toEqual({
+        endpoint: 'https://gitea.renovatebot.com/',
+        gitAuthor: 'Renovate Bot <renovate@example.com>',
+      });
     });
 
     it('should support custom endpoint including api path', async () => {
-      helper.getCurrentUser.mockResolvedValueOnce(mockUser);
+      const scope = httpMock.scope('https://gitea.renovatebot.com/api/v1');
+      scope
+        .get('/user')
+        .reply(200, mockUser)
+        .get('/version')
+        .reply(200, { version: GITEA_VERSION });
 
       expect(
         await gitea.initPlatform({
           token: 'some-token',
-          endpoint: 'https://gitea.renovatebot.com/api/v1',
+          endpoint: 'https://gitea.renovatebot.com',
         }),
-      ).toMatchObject({
+      ).toEqual({
         endpoint: 'https://gitea.renovatebot.com/',
+        gitAuthor: 'Renovate Bot <renovate@example.com>',
       });
     });
 
     it('should use username as author name if full name is missing', async () => {
-      helper.getCurrentUser.mockResolvedValueOnce({
-        ...mockUser,
-        full_name: undefined,
-      });
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      scope
+        .get('/user')
+        .reply(200, {
+          ...mockUser,
+          full_name: undefined,
+        })
+        .get('/version')
+        .reply(200, { version: GITEA_VERSION });
 
-      expect(
-        await gitea.initPlatform({ token: 'some-token' }),
-      ).toMatchSnapshot();
+      expect(await gitea.initPlatform({ token: 'some-token' })).toEqual({
+        endpoint: 'https://gitea.com/',
+        gitAuthor: 'renovate <renovate@example.com>',
+      });
     });
   });
 
   describe('getRepos', () => {
     it('should propagate any other errors', async () => {
-      helper.searchRepos.mockRejectedValueOnce(new Error('searchRepos()'));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/search')
+        .query({
+          uid: 1,
+          archived: false,
+        })
+        .replyWithError(new Error('searchRepos()'));
+      await initFakePlatform(scope);
 
       await expect(gitea.getRepos()).rejects.toThrow('searchRepos()');
     });
 
     it('should return an array of repos', async () => {
-      helper.searchRepos.mockResolvedValueOnce(mockRepos);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/search')
+        .query({
+          uid: 1,
+          archived: false,
+        })
+        .reply(200, {
+          ok: true,
+          data: mockRepos,
+        });
+      await initFakePlatform(scope);
 
       const repos = await gitea.getRepos();
       expect(repos).toEqual(['a/b', 'c/d']);
-      expect(helper.searchRepos).toHaveBeenCalledWith({
-        uid: undefined,
-        archived: false,
-      });
     });
 
     it('Sorts repos', async () => {
       process.env.RENOVATE_X_AUTODISCOVER_REPO_SORT = 'updated';
       process.env.RENOVATE_X_AUTODISCOVER_REPO_ORDER = 'desc';
-      helper.searchRepos.mockResolvedValueOnce(mockRepos);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/search')
+        .query({
+          uid: 1,
+          archived: false,
+          sort: 'updated',
+          order: 'desc',
+        })
+        .reply(200, {
+          ok: true,
+          data: mockRepos,
+        });
+      await initFakePlatform(scope);
 
       const repos = await gitea.getRepos();
       expect(repos).toEqual(['a/b', 'c/d']);
-
-      expect(helper.searchRepos).toHaveBeenCalledWith({
-        uid: undefined,
-        archived: false,
-        sort: 'updated',
-        order: 'desc',
-      });
     });
   });
 
@@ -336,119 +375,208 @@ describe('modules/platform/gitea/index', () => {
     };
 
     it('should propagate API errors', async () => {
-      helper.getRepo.mockRejectedValueOnce(new Error('getRepo()'));
-
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .replyWithError(new Error('getRepo()'));
+      await initFakePlatform(scope);
       await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow('getRepo()');
     });
 
     it('should abort when repo is archived', async () => {
-      await expect(initFakeRepo({ archived: true })).rejects.toThrow(
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          archived: true,
+        });
+      await initFakePlatform(scope);
+      await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow(
         REPOSITORY_ARCHIVED,
       );
     });
 
     it('should abort when repo is mirrored', async () => {
-      await expect(initFakeRepo({ mirror: true })).rejects.toThrow(
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          mirror: true,
+        });
+      await initFakePlatform(scope);
+      await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow(
         REPOSITORY_MIRRORED,
       );
     });
 
     it('should abort when repo is empty', async () => {
-      await expect(initFakeRepo({ empty: true })).rejects.toThrow(
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          empty: true,
+        });
+      await initFakePlatform(scope);
+      await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow(
         REPOSITORY_EMPTY,
       );
     });
 
     it('should abort when repo has insufficient permissions', async () => {
-      await expect(
-        initFakeRepo({
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
           permissions: {
             pull: false,
             push: false,
             admin: false,
           },
-        }),
-      ).rejects.toThrow(REPOSITORY_ACCESS_FORBIDDEN);
+        });
+      await initFakePlatform(scope);
+      await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow(
+        REPOSITORY_ACCESS_FORBIDDEN,
+      );
     });
 
     it('should abort when repo has no available merge methods', async () => {
-      await expect(initFakeRepo({ allow_rebase: false })).rejects.toThrow(
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          allow_rebase: false,
+        });
+      await initFakePlatform(scope);
+      await expect(gitea.initRepo(initRepoCfg)).rejects.toThrow(
         REPOSITORY_BLOCKED,
       );
     });
 
     it('should fall back to merge method "rebase-merge"', async () => {
-      expect(
-        await initFakeRepo({
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
           allow_rebase: false,
           allow_rebase_explicit: true,
+        });
+      await initFakePlatform(scope);
+
+      await gitea.initRepo(initRepoCfg);
+
+      expect(git.initRepo).toHaveBeenCalledExactlyOnceWith(
+        expect.objectContaining({
+          mergeMethod: 'rebase-merge',
         }),
-      ).toMatchSnapshot();
+      );
     });
 
     it('should fall back to merge method "squash"', async () => {
-      expect(
-        await initFakeRepo({ allow_rebase: false, allow_squash_merge: true }),
-      ).toMatchSnapshot();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          allow_rebase: false,
+          allow_squash_merge: true,
+        });
+      await initFakePlatform(scope);
+
+      await gitea.initRepo(initRepoCfg);
+
+      expect(git.initRepo).toHaveBeenCalledExactlyOnceWith(
+        expect.objectContaining({
+          mergeMethod: 'squash',
+        }),
+      );
     });
 
     it('should fall back to merge method "merge"', async () => {
-      expect(
-        await initFakeRepo({
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
           allow_rebase: false,
           allow_merge_commits: true,
+        });
+      await initFakePlatform(scope);
+
+      await gitea.initRepo(initRepoCfg);
+
+      expect(git.initRepo).toHaveBeenCalledExactlyOnceWith(
+        expect.objectContaining({
+          mergeMethod: 'merge',
         }),
-      ).toMatchSnapshot();
+      );
     });
 
     it('should use clone_url of repo if gitUrl is not specified', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
       };
       await gitea.initRepo(repoCfg);
 
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({ url: mockRepo.clone_url }),
       );
     });
 
     it('should use clone_url of repo if gitUrl has value default', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
         gitUrl: 'default',
       };
       await gitea.initRepo(repoCfg);
 
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({ url: mockRepo.clone_url }),
       );
     });
 
     it('should use ssh_url of repo if gitUrl has value ssh', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
         gitUrl: 'ssh',
       };
       await gitea.initRepo(repoCfg);
 
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({ url: mockRepo.ssh_url }),
       );
     });
 
     it('should abort when gitUrl has value ssh but ssh_url is empty', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, { ...mockRepo, ssh_url: undefined });
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce({ ...mockRepo, ssh_url: undefined });
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
         gitUrl: 'ssh',
@@ -460,16 +588,19 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should use generated url of repo if gitUrl has value endpoint', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
         gitUrl: 'endpoint',
       };
       await gitea.initRepo(repoCfg);
 
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({
           url: `https://gitea.com/${mockRepo.full_name}.git`,
         }),
@@ -477,12 +608,15 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should abort when clone_url is empty', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          clone_url: undefined,
+        });
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce({
-        ...mockRepo,
-        clone_url: undefined,
-      });
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
       };
@@ -493,7 +627,11 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should use given access token if gitUrl has value endpoint', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
       const token = 'abc';
       hostRules.add({
@@ -502,7 +640,6 @@ describe('modules/platform/gitea/index', () => {
         token,
       });
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
         gitUrl: 'endpoint',
@@ -511,7 +648,7 @@ describe('modules/platform/gitea/index', () => {
 
       const url = new URL(`${mockRepo.clone_url}`);
       url.username = token;
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({
           url: `https://${token}@gitea.com/${mockRepo.full_name}.git`,
         }),
@@ -519,7 +656,11 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should use given access token if gitUrl is not specified', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, mockRepo);
+      await initFakePlatform(scope);
 
       const token = 'abc';
       hostRules.add({
@@ -528,7 +669,6 @@ describe('modules/platform/gitea/index', () => {
         token,
       });
 
-      helper.getRepo.mockResolvedValueOnce(mockRepo);
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
       };
@@ -536,18 +676,21 @@ describe('modules/platform/gitea/index', () => {
 
       const url = new URL(`${mockRepo.clone_url}`);
       url.username = token;
-      expect(gitvcs.initRepo).toHaveBeenCalledWith(
+      expect(git.initRepo).toHaveBeenCalledWith(
         expect.objectContaining({ url: url.toString() }),
       );
     });
 
     it('should abort when clone_url is not valid', async () => {
-      expect.assertions(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get(`/repos/${initRepoCfg.repository}`)
+        .reply(200, {
+          ...mockRepo,
+          clone_url: 'abc',
+        });
+      await initFakePlatform(scope);
 
-      helper.getRepo.mockResolvedValueOnce({
-        ...mockRepo,
-        clone_url: 'abc',
-      });
       const repoCfg: RepoParams = {
         repository: mockRepo.full_name,
       };
@@ -559,101 +702,190 @@ describe('modules/platform/gitea/index', () => {
   });
 
   describe('setBranchStatus', () => {
-    const setBranchStatus = async (bsc?: Partial<BranchStatusConfig>) => {
-      await initFakeRepo();
-      await gitea.setBranchStatus({
-        branchName: 'some-branch',
-        state: 'green',
-        context: 'some-context',
-        description: 'some-description',
-        ...bsc,
-      });
-    };
-
     it('should create a new commit status', async () => {
-      await setBranchStatus();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post(
+          '/repos/some/repo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
+          {
+            state: 'success',
+            context: 'some-context',
+            description: 'some-description',
+          },
+        )
+        .reply(200)
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, []);
 
-      expect(helper.createCommitStatus).toHaveBeenCalledTimes(1);
-      expect(helper.createCommitStatus).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockCommitHash,
-        {
-          state: 'success',
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(
+        gitea.setBranchStatus({
+          branchName: 'some-branch',
+          state: 'green',
           context: 'some-context',
           description: 'some-description',
-        },
-      );
+        }),
+      ).toResolve();
     });
 
     it('should default to pending state', async () => {
-      await setBranchStatus({ state: undefined });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post(
+          '/repos/some/repo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
+          {
+            state: 'pending',
+            context: 'some-context',
+            description: 'some-description',
+          },
+        )
+        .reply(200)
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, []);
 
-      expect(helper.createCommitStatus).toHaveBeenCalledTimes(1);
-      expect(helper.createCommitStatus).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockCommitHash,
-        {
-          state: 'pending',
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(
+        gitea.setBranchStatus({
+          branchName: 'some-branch',
           context: 'some-context',
           description: 'some-description',
-        },
-      );
+          state: undefined as never,
+        }),
+      ).toResolve();
     });
 
     it('should include url if specified', async () => {
-      await setBranchStatus({ url: 'some-url' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post(
+          '/repos/some/repo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
+          {
+            state: 'success',
+            context: 'some-context',
+            description: 'some-description',
+            target_url: 'some-url',
+          },
+        )
+        .reply(200)
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, []);
 
-      expect(helper.createCommitStatus).toHaveBeenCalledTimes(1);
-      expect(helper.createCommitStatus).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockCommitHash,
-        {
-          state: 'success',
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(
+        gitea.setBranchStatus({
+          branchName: 'some-branch',
+          state: 'green',
           context: 'some-context',
           description: 'some-description',
-          target_url: 'some-url',
-        },
-      );
+          url: 'some-url',
+        }),
+      ).toResolve();
     });
 
     it('should gracefully fail with warning', async () => {
-      helper.createCommitStatus.mockRejectedValueOnce(new Error());
-      await setBranchStatus();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post(
+          '/repos/some/repo/statuses/0d9c7726c3d628b7e28af234595cfd20febdbf8e',
+        )
+        .replyWithError('unknown error');
 
-      expect(logger.warn).toHaveBeenCalledTimes(1);
-    });
-  });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-  describe('getBranchStatus', () => {
-    const getBranchStatus = async (state: string): Promise<BranchStatus> => {
-      await initFakeRepo();
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          worstStatus: state as CommitStatusType,
+      await expect(
+        gitea.setBranchStatus({
+          branchName: 'some-branch',
+          state: 'green',
+          context: 'some-context',
+          description: 'some-description',
         }),
+      ).toResolve();
+
+      expect(logger.warn).toHaveBeenCalledWith(
+        {
+          err: expect.any(Error),
+        },
+        'Failed to set branch status',
       );
+    });
+  });
 
-      return gitea.getBranchStatus('some-branch', true);
-    };
+  describe('getBranchStatus', () => {
+    const commitStatus = (status: CommitStatusType): CommitStatus => ({
+      id: 1,
+      status,
+      context: '',
+      description: '',
+      target_url: '',
+      created_at: '',
+    });
 
     it('should return yellow for unknown result', async () => {
-      expect(await getBranchStatus('unknown')).toBe('yellow');
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [commitStatus('unknown')]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', true);
+
+      expect(res).toBe('yellow');
     });
 
     it('should return pending state for pending result', async () => {
-      expect(await getBranchStatus('pending')).toBe('yellow');
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [commitStatus('pending')]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', true);
+
+      expect(res).toBe('yellow');
     });
 
-    it('should return success state for success result', async () => {
-      expect(await getBranchStatus('success')).toBe('green');
+    it('should return green state for success result', async () => {
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [commitStatus('success')]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', true);
+
+      expect(res).toBe('green');
     });
 
-    it('should return null for all other results', async () => {
-      expect(await getBranchStatus('invalid')).toBe('yellow');
+    it('should return yellow for all other results', async () => {
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [commitStatus('invalid' as never)]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', true);
+
+      expect(res).toBe('yellow');
     });
 
     it('should abort when branch status returns 404', async () => {
-      helper.getCombinedCommitStatus.mockRejectedValueOnce({ statusCode: 404 });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(404);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       await expect(gitea.getBranchStatus('some-branch', true)).rejects.toThrow(
         REPOSITORY_CHANGED,
@@ -661,19 +893,23 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should propagate any other errors', async () => {
-      helper.getCombinedCommitStatus.mockRejectedValueOnce(
-        new Error('getCombinedCommitStatus()'),
-      );
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .replyWithError('unknown error');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       await expect(gitea.getBranchStatus('some-branch', true)).rejects.toThrow(
-        'getCombinedCommitStatus()',
+        'unknown error',
       );
     });
 
     it('should treat internal checks as success', async () => {
-      helper.getCombinedCommitStatus.mockResolvedValueOnce({
-        worstStatus: 'success',
-        statuses: [
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [
           {
             id: 1,
             status: 'success',
@@ -682,39 +918,46 @@ describe('modules/platform/gitea/index', () => {
             target_url: '',
             created_at: '',
           },
-        ],
-      });
-      expect(await gitea.getBranchStatus('some-branch', true)).toBe('green');
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', true);
+
+      expect(res).toBe('green');
     });
 
     it('should not treat internal checks as success', async () => {
-      await initFakeRepo();
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          worstStatus: 'success',
-          statuses: [
-            {
-              id: 1,
-              status: 'success',
-              context: 'renovate/stability-days',
-              description: 'internal check',
-              target_url: '',
-              created_at: '',
-            },
-          ],
-        }),
-      );
-      expect(await gitea.getBranchStatus('some-branch', false)).toBe('yellow');
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [
+          {
+            id: 1,
+            status: 'success',
+            context: 'renovate/stability-days',
+            description: 'internal check',
+            target_url: '',
+            created_at: '',
+          },
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatus('some-branch', false);
+
+      expect(res).toBe('yellow');
     });
   });
 
   describe('getBranchStatusCheck', () => {
     it('should return null with no results', async () => {
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          statuses: [],
-        }),
-      );
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, []);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       expect(
         await gitea.getBranchStatusCheck('some-branch', 'some-context'),
@@ -722,241 +965,341 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should return null with no matching results', async () => {
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          statuses: [partial<CommitStatus>({ context: 'other-context' })],
-        }),
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [
+          {
+            id: 1,
+            status: 'success',
+            context: 'other-context',
+            description: 'internal check',
+            target_url: '',
+            created_at: '',
+          },
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatusCheck(
+        'some-branch',
+        'some-context',
       );
 
-      expect(
-        await gitea.getBranchStatusCheck('some-branch', 'some-context'),
-      ).toBeNull();
+      expect(res).toBeNull();
     });
 
     it('should return yellow with unknown status', async () => {
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          statuses: [
-            partial<CommitStatus>({
-              context: 'some-context',
-            }),
-          ],
-        }),
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [
+          {
+            id: 1,
+            status: 'xyz',
+            context: 'some-context',
+            description: '',
+            target_url: '',
+            created_at: '',
+          },
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatusCheck(
+        'some-branch',
+        'some-context',
       );
 
-      expect(
-        await gitea.getBranchStatusCheck('some-branch', 'some-context'),
-      ).toBe('yellow');
+      expect(res).toBe('yellow');
     });
 
     it('should return green of matching result', async () => {
-      helper.getCombinedCommitStatus.mockResolvedValueOnce(
-        partial<CombinedCommitStatus>({
-          statuses: [
-            partial<CommitStatus>({
-              status: 'success',
-              context: 'some-context',
-            }),
-          ],
-        }),
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/commits/some-branch/statuses')
+        .reply(200, [
+          {
+            id: 1,
+            status: 'success',
+            context: 'some-context',
+            description: '',
+            target_url: '',
+            created_at: '',
+          },
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getBranchStatusCheck(
+        'some-branch',
+        'some-context',
       );
 
-      expect(
-        await gitea.getBranchStatusCheck('some-branch', 'some-context'),
-      ).toBe('green');
+      expect(res).toBe('green');
     });
   });
 
   describe('getPrList', () => {
     it('should return list of pull requests', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res = await gitea.getPrList();
-      expect(res).toHaveLength(mockPRs.length);
-      expect(res).toMatchSnapshot();
+      expect(res).toMatchObject([
+        { number: 1, title: 'Some PR' },
+        { number: 2, title: 'Other PR' },
+        { number: 3, title: 'Draft PR' },
+      ]);
     });
 
     it('should filter list by creator', async () => {
-      helper.getCurrentUser.mockResolvedValueOnce(mockUser);
-
-      expect(
-        await gitea.initPlatform({ token: 'some-token' }),
-      ).toMatchSnapshot();
+      const thirdPartyPr = partial<PR>({
+        number: 42,
+        title: 'Third-party PR',
+        body: 'other random pull request',
+        state: 'open',
+        diff_url: 'https://gitea.renovatebot.com/some/repo/pulls/3.diff',
+        created_at: '2011-08-18T22:30:38Z',
+        closed_at: '2016-01-09T10:03:21Z',
+        mergeable: true,
+        base: { ref: 'third-party-base-branch' },
+        head: {
+          label: 'other-head-branch',
+          sha: 'other-head-sha' as LongCommitSha,
+          repo: partial<Repo>({ full_name: mockRepo.full_name }),
+        },
+        user: { username: 'not-renovate' },
+      });
+
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, [
+          thirdPartyPr,
+          ...mockPRs.map((pr) => ({
+            ...pr,
+            user: { username: 'renovate' },
+          })),
+        ]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
+      const res = await gitea.getPrList();
 
-      helper.searchPRs.mockResolvedValueOnce([
-        partial<PR>({
-          number: 3,
-          title: 'Third-party PR',
-          body: 'other random pull request',
-          state: 'open',
-          diff_url: 'https://gitea.renovatebot.com/some/repo/pulls/3.diff',
-          created_at: '2011-08-18T22:30:38Z',
-          closed_at: '2016-01-09T10:03:21Z',
-          mergeable: true,
-          base: { ref: 'third-party-base-branch' },
-          head: {
-            label: 'other-head-branch',
-            sha: 'other-head-sha' as LongCommitSha,
-            repo: partial<Repo>({ full_name: mockRepo.full_name }),
-          },
-          user: { username: 'not-renovate' },
-        }),
-        ...mockPRs.map((pr) => ({ ...pr, user: { username: 'renovate' } })),
+      expect(res).toMatchObject([
+        { number: 1, title: 'Some PR' },
+        { number: 2, title: 'Other PR' },
+        { number: 3, title: 'Draft PR' },
       ]);
-
-      const res = await gitea.getPrList();
-      expect(res).toHaveLength(mockPRs.length);
-      expect(res).toMatchSnapshot();
     });
 
     it('should cache results after first query', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res1 = await gitea.getPrList();
       const res2 = await gitea.getPrList();
+
       expect(res1).toEqual(res2);
-      expect(helper.searchPRs).toHaveBeenCalledTimes(1);
     });
   });
 
   describe('getPr', () => {
     it('should return enriched pull request which exists if open', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      helper.getBranch.mockResolvedValueOnce(
-        partial<Branch>({
-          commit: {
-            id: mockCommitHash,
-            author: partial<CommitUser>({
-              email: 'renovate@whitesourcesoftware.com',
-            }),
-          },
-        }),
-      );
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getPr(1);
 
-      const res = await gitea.getPr(mockPR.number);
-      expect(res).toHaveProperty('number', mockPR.number);
-      expect(res).toMatchSnapshot();
+      expect(res).toMatchObject({ number: 1, title: 'Some PR' });
     });
 
     it('should fallback to direct fetching if cache fails', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce([]);
-      helper.getPR.mockResolvedValueOnce({ ...mockPR, mergeable: false });
-      await initFakeRepo();
+      const pr = mockPRs.find((pr) => pr.number === 1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, [])
+        .get('/repos/some/repo/pulls/1')
+        .reply(200, pr);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      const res = await gitea.getPr(mockPR.number);
-      expect(res).toHaveProperty('number', mockPR.number);
-      expect(res).toMatchSnapshot();
-      expect(helper.getPR).toHaveBeenCalledTimes(1);
-    });
+      const res = await gitea.getPr(1);
 
-    it('should return null for missing pull request', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-
-      expect(await gitea.getPr(42)).toBeNull();
+      expect(res).toMatchObject({ number: 1, title: 'Some PR' });
     });
 
-    it('should block modified pull request for rebasing', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+    it('should return null for missing pull request', async () => {
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, [])
+        .get('/repos/some/repo/pulls/42')
+        .reply(200); // TODO: 404 should be handled
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.getPr(42);
 
-      const res = await gitea.getPr(mockPR.number);
-      expect(res).toHaveProperty('number', mockPR.number);
+      expect(res).toBeNull();
     });
   });
 
   describe('findPr', () => {
     it('should find pull request without title or state', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      const res = await gitea.findPr({ branchName: mockPR.head.label });
-      expect(res).toHaveProperty('sourceBranch', mockPR.head.label);
+      const res = await gitea.findPr({ branchName: 'some-head-branch' });
+
+      expect(res).toMatchObject({
+        number: 1,
+        sourceBranch: 'some-head-branch',
+      });
     });
 
     it('should find pull request with title', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res = await gitea.findPr({
-        branchName: mockPR.head.label,
-        prTitle: mockPR.title,
+        branchName: 'some-head-branch',
+        prTitle: 'Some PR',
+      });
+
+      expect(res).toMatchObject({
+        number: 1,
+        title: 'Some PR',
       });
-      expect(res).toHaveProperty('sourceBranch', mockPR.head.label);
-      expect(res).toHaveProperty('title', mockPR.title);
     });
 
     it('should find pull request with state', async () => {
-      const mockPR = mockPRs[1];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res = await gitea.findPr({
-        branchName: mockPR.head.label,
-        state: mockPR.state,
+        branchName: 'some-head-branch',
+        state: 'open',
+      });
+
+      expect(res).toMatchObject({
+        number: 1,
+        state: 'open',
       });
-      expect(res).toHaveProperty('sourceBranch', mockPR.head.label);
-      expect(res).toHaveProperty('state', mockPR.state);
     });
 
     it('should not find pull request with inverted state', async () => {
-      const mockPR = mockPRs[1];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(
-        await gitea.findPr({
-          branchName: mockPR.head.label,
-          state: `!${mockPR.state as PrState}` as never, // wrong argument being passed intentionally
-        }),
-      ).toBeNull();
+      const res = await gitea.findPr({
+        branchName: 'other-head-branch',
+        state: `!open`,
+      });
+
+      expect(res).toMatchObject({
+        number: 2,
+        state: 'closed',
+        title: 'Other PR',
+      });
     });
 
     it('should find pull request with title and state', async () => {
-      const mockPR = mockPRs[1];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res = await gitea.findPr({
-        branchName: mockPR.head.label,
-        prTitle: mockPR.title,
-        state: mockPR.state,
+        branchName: 'other-head-branch',
+        prTitle: 'Other PR',
+        state: 'closed',
+      });
+
+      expect(res).toMatchObject({
+        number: 2,
+        state: 'closed',
+        title: 'Other PR',
       });
-      expect(res).toHaveProperty('sourceBranch', mockPR.head.label);
-      expect(res).toHaveProperty('title', mockPR.title);
-      expect(res).toHaveProperty('state', mockPR.state);
     });
 
     it('should find pull request with draft', async () => {
-      const mockPR = mockPRs[2];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       const res = await gitea.findPr({
-        branchName: mockPR.head.label,
+        branchName: 'draft-head-branch',
         prTitle: 'Draft PR',
-        state: mockPR.state,
+        state: 'open',
+      });
+
+      expect(res).toMatchObject({
+        number: 3,
+        title: 'Draft PR',
+        isDraft: true,
       });
-      expect(res).toHaveProperty('sourceBranch', mockPR.head.label);
-      expect(res).toHaveProperty('title', 'Draft PR');
-      expect(res).toHaveProperty('state', mockPR.state);
     });
 
     it('should return null for missing pull request', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.findPr({ branchName: 'missing' });
 
-      expect(await gitea.findPr({ branchName: 'missing' })).toBeNull();
+      expect(res).toBeNull();
     });
   });
 
@@ -981,12 +1324,16 @@ describe('modules/platform/gitea/index', () => {
     };
 
     it('should use base branch by default', async () => {
-      helper.createPR.mockResolvedValueOnce({
-        ...mockNewPR,
-        base: { ref: 'devel' },
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, {
+          ...mockNewPR,
+          base: { ref: 'devel' },
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'devel',
@@ -994,23 +1341,20 @@ describe('modules/platform/gitea/index', () => {
         prBody: mockNewPR.body,
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(res).toHaveProperty('targetBranch', 'devel');
-      expect(res).toMatchSnapshot();
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: 'devel',
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
     });
 
     it('should use default branch if requested', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1019,50 +1363,49 @@ describe('modules/platform/gitea/index', () => {
         draftPR: true,
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref);
-      expect(res).toMatchSnapshot();
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: `WIP: ${mockNewPR.title}`,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
     });
 
     it('should resolve and apply optional labels to pull request', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
-      helper.getRepoLabels.mockResolvedValueOnce(mockRepoLabels);
-      helper.getOrgLabels.mockResolvedValueOnce(mockOrgLabels);
-
-      const mockLabels = mockRepoLabels.concat(mockOrgLabels);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR)
+        .get('/repos/some/repo/labels')
+        .reply(200, mockRepoLabels)
+        .get('/orgs/some/labels')
+        .reply(200, mockOrgLabels);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
-      await gitea.createPr({
+      const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
         prTitle: mockNewPR.title,
         prBody: mockNewPR.body,
-        labels: mockLabels.map((l) => l.name),
+        labels: [...mockRepoLabels, ...mockOrgLabels].map(({ name }) => name),
       });
 
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: mockLabels.map((l) => l.id),
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
     });
 
     it('should ensure new pull request gets added to cached pull requests', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       await gitea.getPrList();
       await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
@@ -1070,17 +1413,25 @@ describe('modules/platform/gitea/index', () => {
         prTitle: mockNewPR.title,
         prBody: mockNewPR.body,
       });
-      const res = gitea.getPr(mockNewPR.number);
+      const res = await gitea.getPr(mockNewPR.number);
 
-      expect(res).not.toBeNull();
-      expect(helper.searchPRs).toHaveBeenCalledTimes(1);
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
+      });
     });
 
     it('should attempt to resolve 409 conflict error (w/o update)', async () => {
-      helper.createPR.mockRejectedValueOnce({ statusCode: 409 });
-      helper.searchPRs.mockResolvedValueOnce([mockNewPR]);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(409)
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, [mockNewPR]);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1088,14 +1439,25 @@ describe('modules/platform/gitea/index', () => {
         prBody: mockNewPR.body,
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
+      });
     });
 
     it('should attempt to resolve 409 conflict error (w/ update)', async () => {
-      helper.createPR.mockRejectedValueOnce({ statusCode: 409 });
-      helper.searchPRs.mockResolvedValueOnce([mockNewPR]);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(409)
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, [mockNewPR])
+        .patch('/repos/some/repo/pulls/42')
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1103,19 +1465,20 @@ describe('modules/platform/gitea/index', () => {
         prBody: 'new-body',
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(helper.updatePR).toHaveBeenCalledTimes(1);
-      expect(helper.updatePR).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockNewPR.number,
-        { title: 'new-title', body: 'new-body' },
-      );
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'new-title',
+      });
     });
 
     it('should abort when response for created pull request is invalid', async () => {
-      helper.createPR.mockResolvedValueOnce(partial<PR>());
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, {});
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       await expect(
         gitea.createPr({
           sourceBranch: mockNewPR.head.label,
@@ -1127,9 +1490,15 @@ describe('modules/platform/gitea/index', () => {
     });
 
     it('should use platform automerge', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
-      await initFakePlatform('1.17.0');
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR)
+        .post('/repos/some/repo/pulls/42/merge')
+        .reply(200);
+      await initFakePlatform(scope, '1.17.0');
+      await initFakeRepo(scope);
+
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1138,32 +1507,22 @@ describe('modules/platform/gitea/index', () => {
         platformOptions: { usePlatformAutomerge: true },
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref);
-
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
-      expect(helper.mergePR).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockNewPR.number,
-        {
-          Do: 'rebase',
-          merge_when_checks_succeed: true,
-        },
-      );
     });
 
     it('continues on platform automerge error', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
-      await initFakePlatform('1.17.0');
-      await initFakeRepo();
-      helper.mergePR.mockRejectedValueOnce(new Error('fake'));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR)
+        .post('/repos/some/repo/pulls/42/merge')
+        .replyWithError('unknown error');
+      await initFakePlatform(scope, '1.17.0');
+      await initFakeRepo(scope);
+
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1172,30 +1531,24 @@ describe('modules/platform/gitea/index', () => {
         platformOptions: { usePlatformAutomerge: true },
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref);
-
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
-      expect(helper.mergePR).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockNewPR.number,
-        {
-          Do: 'rebase',
-          merge_when_checks_succeed: true,
-        },
+      expect(logger.warn).toHaveBeenCalledWith(
+        expect.objectContaining({ prNumber: 42 }),
+        'Gitea-native automerge: fail',
       );
     });
 
     it('continues if platform automerge is not supported', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR);
+      await initFakePlatform(scope, '1.10.0');
+      await initFakeRepo(scope);
+
       const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
@@ -1204,25 +1557,27 @@ describe('modules/platform/gitea/index', () => {
         platformOptions: { usePlatformAutomerge: true },
       });
 
-      expect(res).toHaveProperty('number', mockNewPR.number);
-      expect(res).toHaveProperty('targetBranch', mockNewPR.base.ref);
-
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
-      expect(helper.mergePR).not.toHaveBeenCalled();
+      expect(logger.debug).toHaveBeenCalledWith(
+        expect.objectContaining({ prNumber: 42 }),
+        'Gitea-native automerge: not supported on this version of Gitea. Use 1.17.0 or newer.',
+      );
     });
 
     it('should create PR with repository merge method when automergeStrategy is auto', async () => {
-      helper.createPR.mockResolvedValueOnce(mockNewPR);
-      await initFakePlatform('1.17.0');
-      await initFakeRepo();
-      await gitea.createPr({
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls')
+        .reply(200, mockNewPR)
+        .post('/repos/some/repo/pulls/42/merge')
+        .reply(200);
+      await initFakePlatform(scope, '1.17.0');
+      await initFakeRepo(scope);
+
+      const res = await gitea.createPr({
         sourceBranch: mockNewPR.head.label,
         targetBranch: 'master',
         prTitle: mockNewPR.title,
@@ -1233,22 +1588,10 @@ describe('modules/platform/gitea/index', () => {
         },
       });
 
-      expect(helper.createPR).toHaveBeenCalledTimes(1);
-      expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-        base: mockNewPR.base.ref,
-        head: mockNewPR.head.label,
-        title: mockNewPR.title,
-        body: mockNewPR.body,
-        labels: [],
+      expect(res).toMatchObject({
+        number: 42,
+        title: 'pr-title',
       });
-      expect(helper.mergePR).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockNewPR.number,
-        {
-          Do: 'rebase',
-          merge_when_checks_succeed: true,
-        },
-      );
     });
 
     it.each`
@@ -1260,10 +1603,19 @@ describe('modules/platform/gitea/index', () => {
     `(
       'should create PR with mergeStrategy $prMergeStrategy',
       async ({ automergeStrategy, prMergeStrategy }) => {
-        helper.createPR.mockResolvedValueOnce(mockNewPR);
-        await initFakePlatform('1.17.0');
-        await initFakeRepo();
-        await gitea.createPr({
+        const scope = httpMock
+          .scope('https://gitea.com/api/v1')
+          .post('/repos/some/repo/pulls')
+          .reply(200, mockNewPR)
+          .post('/repos/some/repo/pulls/42/merge')
+          .reply(200, {
+            Do: prMergeStrategy,
+            merge_when_checks_succeed: true,
+          });
+        await initFakePlatform(scope, '1.17.0');
+        await initFakeRepo(scope);
+
+        const res = await gitea.createPr({
           sourceBranch: mockNewPR.head.label,
           targetBranch: 'master',
           prTitle: mockNewPR.title,
@@ -1274,185 +1626,256 @@ describe('modules/platform/gitea/index', () => {
           },
         });
 
-        expect(helper.createPR).toHaveBeenCalledTimes(1);
-        expect(helper.createPR).toHaveBeenCalledWith(mockRepo.full_name, {
-          base: mockNewPR.base.ref,
-          head: mockNewPR.head.label,
-          title: mockNewPR.title,
-          body: mockNewPR.body,
-          labels: [],
+        expect(res).toMatchObject({
+          number: 42,
+          title: 'pr-title',
         });
-        expect(helper.mergePR).toHaveBeenCalledWith(
-          mockRepo.full_name,
-          mockNewPR.number,
-          {
-            Do: prMergeStrategy,
-            merge_when_checks_succeed: true,
-          },
-        );
       },
     );
   });
 
   describe('updatePr', () => {
     it('should update pull request with title', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-      await gitea.updatePr({ number: 1, prTitle: 'New Title' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .patch('/repos/some/repo/pulls/1', { title: 'New Title' })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updatePR).toHaveBeenCalledTimes(1);
-      expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        title: 'New Title',
-      });
+      await expect(
+        gitea.updatePr({ number: 1, prTitle: 'New Title' }),
+      ).toResolve();
     });
 
     it('should update pull target branch', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-      await gitea.updatePr({
-        number: 1,
-        prTitle: 'New Title',
-        targetBranch: 'New Base',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .patch('/repos/some/repo/pulls/1', {
+          title: 'New Title',
+          base: 'New Base',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updatePR).toHaveBeenCalledTimes(1);
-      expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        title: 'New Title',
-        base: 'New Base',
-      });
+      await expect(
+        gitea.updatePr({
+          number: 1,
+          prTitle: 'New Title',
+          targetBranch: 'New Base',
+        }),
+      ).toResolve();
     });
 
     it('should update pull request with title and body', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-      await gitea.updatePr({
-        number: 1,
-        prTitle: 'New Title',
-        prBody: 'New Body',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .patch('/repos/some/repo/pulls/1', {
+          title: 'New Title',
+          body: 'New Body',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updatePR).toHaveBeenCalledTimes(1);
-      expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        title: 'New Title',
-        body: 'New Body',
-      });
+      await expect(
+        gitea.updatePr({
+          number: 1,
+          prTitle: 'New Title',
+          prBody: 'New Body',
+        }),
+      ).toResolve();
     });
 
     it('should update pull request with draft', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-      await gitea.updatePr({
-        number: 3,
-        prTitle: 'New Title',
-        prBody: 'New Body',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .patch('/repos/some/repo/pulls/3', {
+          title: 'WIP: New Title',
+          body: 'New Body',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updatePR).toHaveBeenCalledTimes(1);
-      expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 3, {
-        title: 'WIP: New Title',
-        body: 'New Body',
-      });
+      await expect(
+        gitea.updatePr({
+          number: 3,
+          prTitle: 'New Title',
+          prBody: 'New Body',
+        }),
+      ).toResolve();
     });
 
     it('should close pull request', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
-      await gitea.updatePr({
-        number: 1,
-        prTitle: 'New Title',
-        prBody: 'New Body',
-        state: 'closed',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs)
+        .patch('/repos/some/repo/pulls/1', {
+          title: 'New Title',
+          body: 'New Body',
+          state: 'closed',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updatePR).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        title: 'New Title',
-        body: 'New Body',
-        state: 'closed',
-      });
+      await expect(
+        gitea.updatePr({
+          number: 1,
+          prTitle: 'New Title',
+          prBody: 'New Body',
+          state: 'closed',
+        }),
+      ).toResolve();
     });
   });
 
   describe('mergePr', () => {
     it('should return true when merging succeeds', async () => {
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls/1/merge', {
+          Do: 'rebase',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(
-        await gitea.mergePr({
-          branchName: 'some-branch',
-          id: 1,
-        }),
-      ).toBe(true);
-      expect(helper.mergePR).toHaveBeenCalledTimes(1);
-      expect(helper.mergePR).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        Do: 'rebase',
+      const res = await gitea.mergePr({
+        branchName: 'some-branch',
+        id: 1,
       });
+
+      expect(res).toBe(true);
     });
 
     it('should return false when merging fails', async () => {
-      helper.mergePR.mockRejectedValueOnce(new Error());
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls/1/merge', {
+          Do: 'squash',
+        })
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.mergePr({
+        branchName: 'some-branch',
+        id: 1,
+        strategy: 'squash',
+      });
 
-      expect(
-        await gitea.mergePr({
-          branchName: 'some-branch',
-          id: 1,
-          strategy: 'squash',
-        }),
-      ).toBe(false);
+      expect(res).toBe(false);
     });
   });
 
   describe('getIssueList', () => {
     it('should return empty for disabled issues', async () => {
-      await initFakeRepo({ has_issues: false });
-      expect(await gitea.getIssueList()).toBeEmptyArray();
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope, { has_issues: false });
+
+      const res = await gitea.getIssueList();
+
+      expect(res).toBeEmptyArray();
     });
   });
 
   describe('getIssue', () => {
     it('should return the issue', async () => {
       const mockIssue = mockIssues.find((i) => i.number === 1)!;
-      helper.getIssue.mockResolvedValueOnce(mockIssue);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1')
+        .reply(200, mockIssue);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(await gitea.getIssue?.(mockIssue.number)).toHaveProperty(
-        'number',
-        mockIssue.number,
-      );
+      const res = await gitea.getIssue?.(mockIssue.number);
+
+      expect(res).toEqual({
+        body: 'some-content',
+        number: 1,
+      });
     });
 
     it('should return null for disabled issues', async () => {
-      await initFakeRepo({ has_issues: false });
-      expect(await gitea.getIssue!(1)).toBeNull();
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope, { has_issues: false });
+
+      const res = await gitea.getIssue!(1);
+
+      expect(res).toBeNull();
     });
   });
 
   describe('findIssue', () => {
     it('should return existing open issue', async () => {
-      const mockIssue = mockIssues.find((i) => i.title === 'open-issue')!;
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.getIssue.mockResolvedValueOnce(mockIssue);
-      await initFakeRepo();
-
-      expect(await gitea.findIssue(mockIssue.title)).toHaveProperty(
-        'number',
-        mockIssue.number,
-      );
+      const mockIssue = mockIssues.find(({ title }) => title === 'open-issue')!;
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .get('/repos/some/repo/issues/1')
+        .reply(200, mockIssue);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.findIssue(mockIssue.title);
+
+      expect(res).toMatchObject({
+        body: 'some-content',
+        number: 1,
+      });
     });
 
     it('should not return existing closed issue', async () => {
-      const mockIssue = mockIssues.find((i) => i.title === 'closed-issue')!;
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      await initFakeRepo();
+      const mockIssue = mockIssues.find(
+        ({ title }) => title === 'closed-issue',
+      )!;
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      const res = await gitea.findIssue(mockIssue.title);
 
-      expect(await gitea.findIssue(mockIssue.title)).toBeNull();
+      expect(res).toBeNull();
     });
 
     it('should return null for missing issue', async () => {
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(await gitea.findIssue('missing')).toBeNull();
+      const res = await gitea.findIssue('missing');
+
+      expect(res).toBeNull();
     });
   });
 
@@ -1465,18 +1888,22 @@ describe('modules/platform/gitea/index', () => {
         once: false,
       };
 
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.createIssue.mockResolvedValueOnce(partial<Issue>({ number: 42 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .post('/repos/some/repo/issues', {
+          body: mockIssue.body,
+          title: mockIssue.title,
+        })
+        .reply(200, { number: 42 });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue(mockIssue);
 
       expect(res).toBe('created');
-      expect(helper.createIssue).toHaveBeenCalledTimes(1);
-      expect(helper.createIssue).toHaveBeenCalledWith(mockRepo.full_name, {
-        body: mockIssue.body,
-        title: mockIssue.title,
-      });
     });
 
     it('should create issue with the correct labels', async () => {
@@ -1487,35 +1914,49 @@ describe('modules/platform/gitea/index', () => {
         once: false,
         labels: ['Renovate', 'Maintenance'],
       };
-      const mockLabels: Label[] = [
-        partial<Label>({ id: 1, name: 'Renovate' }),
-        partial<Label>({ id: 3, name: 'Maintenance' }),
-      ];
 
-      helper.getRepoLabels.mockResolvedValueOnce(partial(mockLabels));
-      helper.getOrgLabels.mockResolvedValueOnce([]);
-
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.createIssue.mockResolvedValueOnce(partial<Issue>({ number: 42 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .get('/repos/some/repo/labels')
+        .reply(200, [
+          partial<Label>({ id: 1, name: 'Renovate' }),
+          partial<Label>({ id: 3, name: 'Maintenance' }),
+        ] satisfies Label[])
+        .get('/orgs/some/labels')
+        .reply(200, mockOrgLabels)
+        .post('/repos/some/repo/issues', {
+          body: 'new-body',
+          title: 'new-title',
+          labels: [1, 3],
+        })
+        .reply(200, { number: 42 });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue(mockIssue);
 
       expect(res).toBe('created');
-      expect(helper.createIssue).toHaveBeenCalledTimes(1);
-      expect(helper.createIssue).toHaveBeenCalledWith(mockRepo.full_name, {
-        body: mockIssue.body,
-        title: mockIssue.title,
-        labels: [1, 3],
-      });
     });
 
     it('should not reopen closed issue by default', async () => {
       const closedIssue = mockIssues.find((i) => i.title === 'closed-issue')!;
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.updateIssue.mockResolvedValueOnce(closedIssue);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .patch('/repos/some/repo/issues/2', {
+          body: closedIssue.body,
+          state: closedIssue.state,
+          title: 'closed-issue',
+        })
+        .reply(200, closedIssue);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: closedIssue.title,
         body: closedIssue.body,
@@ -1524,16 +1965,6 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe('updated');
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssue).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        closedIssue.number,
-        {
-          body: closedIssue.body,
-          state: closedIssue.state,
-          title: 'closed-issue',
-        },
-      );
     });
 
     it('should not update labels when not necessary', async () => {
@@ -1550,12 +1981,20 @@ describe('modules/platform/gitea/index', () => {
         state: 'open',
       };
 
-      helper.getRepoLabels.mockResolvedValueOnce(partial(mockLabels));
-      helper.getOrgLabels.mockResolvedValueOnce([]);
-      helper.searchIssues.mockResolvedValueOnce([mockIssue]);
-      helper.updateIssue.mockResolvedValueOnce(mockIssue);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, [mockIssue])
+        .patch('/repos/some/repo/issues/10')
+        .reply(200, mockIssue)
+        .get('/repos/some/repo/labels')
+        .reply(200, mockLabels)
+        .get('/orgs/some/labels')
+        .reply(200, []);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: mockIssue.title,
         body: 'new-body',
@@ -1563,8 +2002,6 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe('updated');
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssueLabels).toHaveBeenCalledTimes(0);
     });
 
     it('should update labels when missing', async () => {
@@ -1581,12 +2018,22 @@ describe('modules/platform/gitea/index', () => {
         state: 'open',
       };
 
-      helper.getRepoLabels.mockResolvedValueOnce(partial(mockLabels));
-      helper.getOrgLabels.mockResolvedValueOnce([]);
-      helper.searchIssues.mockResolvedValueOnce([mockIssue]);
-      helper.updateIssue.mockResolvedValueOnce(mockIssue);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, [mockIssue])
+        .patch('/repos/some/repo/issues/10')
+        .reply(200, mockIssue)
+        .get('/repos/some/repo/labels')
+        .reply(200, mockLabels)
+        .get('/orgs/some/labels')
+        .reply(200, [])
+        .put('/repos/some/repo/issues/10/labels', { labels: [1, 3] })
+        .reply(200, mockLabels);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: mockIssue.title,
         body: 'new-body',
@@ -1594,15 +2041,6 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe('updated');
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssueLabels).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssueLabels).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockIssue.number,
-        {
-          labels: [1, 3],
-        },
-      );
     });
 
     it('should reset labels when others have been set', async () => {
@@ -1620,12 +2058,22 @@ describe('modules/platform/gitea/index', () => {
         state: 'open',
       };
 
-      helper.getRepoLabels.mockResolvedValueOnce(partial(mockLabels));
-      helper.getOrgLabels.mockResolvedValueOnce([]);
-      helper.searchIssues.mockResolvedValueOnce([mockIssue]);
-      helper.updateIssue.mockResolvedValueOnce(mockIssue);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, [mockIssue])
+        .patch('/repos/some/repo/issues/10')
+        .reply(200, mockIssue)
+        .get('/repos/some/repo/labels')
+        .reply(200, mockLabels)
+        .get('/orgs/some/labels')
+        .reply(200, [])
+        .put('/repos/some/repo/issues/10/labels', { labels: [1, 3] })
+        .reply(200, mockLabels);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: mockIssue.title,
         body: 'new-body',
@@ -1633,23 +2081,24 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe('updated');
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssueLabels).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssueLabels).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockIssue.number,
-        {
-          labels: [1, 3],
-        },
-      );
     });
 
     it('should reopen closed issue if desired', async () => {
       const closedIssue = mockIssues.find((i) => i.title === 'closed-issue')!;
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.updateIssue.mockResolvedValueOnce(closedIssue);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .patch('/repos/some/repo/issues/2', {
+          body: closedIssue.body,
+          state: 'open',
+          title: 'closed-issue',
+        })
+        .reply(200, { ...closedIssue, state: 'open' });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: closedIssue.title,
         body: closedIssue.body,
@@ -1658,23 +2107,18 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe('updated');
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssue).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        closedIssue.number,
-        {
-          body: closedIssue.body,
-          state: 'open',
-          title: 'closed-issue',
-        },
-      );
     });
 
     it('should not update existing closed issue if desired', async () => {
       const closedIssue = mockIssues.find((i) => i.title === 'closed-issue')!;
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
         title: closedIssue.title,
         body: closedIssue.body,
@@ -1683,58 +2127,72 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBeNull();
-      expect(helper.updateIssue).not.toHaveBeenCalled();
     });
 
     it('should close all open duplicate issues except first one when updating', async () => {
       const duplicates = mockIssues.filter(
         (i) => i.title === 'duplicate-issue',
       );
-      const firstDuplicate = duplicates[0];
-      helper.searchIssues.mockResolvedValueOnce(duplicates);
+      const [first, second, third] = duplicates;
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, duplicates)
+        .patch(`/repos/some/repo/issues/${second.number}`, {
+          state: 'closed',
+        })
+        .reply(200, { ...second, state: 'closed' })
+        .patch(`/repos/some/repo/issues/${third.number}`, {
+          state: 'closed',
+        })
+        .reply(200, { ...third, state: 'closed' });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureIssue({
-        title: firstDuplicate.title,
-        body: firstDuplicate.body,
+        title: first.title,
+        body: first.body,
         shouldReOpen: false,
         once: false,
       });
 
       expect(res).toBeNull();
-      expect(helper.closeIssue).toHaveBeenCalledTimes(duplicates.length - 1);
-      for (const issue of duplicates) {
-        if (issue.number !== firstDuplicate.number) {
-          // eslint-disable-next-line jest/no-conditional-expect
-          expect(helper.closeIssue).toHaveBeenCalledWith(
-            mockRepo.full_name,
-            issue.number,
-          );
-        }
-      }
-      expect(helper.updateIssue).not.toHaveBeenCalled();
     });
 
     it('should reset issue cache when creating an issue', async () => {
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      helper.createIssue.mockResolvedValueOnce(partial<Issue>({ number: 42 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .twice()
+        .reply(200, mockIssues)
+        .post('/repos/some/repo/issues')
+        .reply(200, { number: 42 });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
-      await gitea.ensureIssue({
-        title: 'new-title',
-        body: 'new-body',
-        shouldReOpen: false,
-        once: false,
-      });
-      await gitea.getIssueList();
+      await expect(
+        gitea.ensureIssue({
+          title: 'new-title',
+          body: 'new-body',
+          shouldReOpen: false,
+          once: false,
+        }),
+      ).resolves.toBe('created');
 
-      expect(helper.searchIssues).toHaveBeenCalledTimes(2);
+      await expect(gitea.getIssueList()).toResolve();
     });
 
     it('should gracefully fail with warning', async () => {
-      helper.searchIssues.mockRejectedValueOnce(new Error());
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       await gitea.ensureIssue({
         title: 'new-title',
         body: 'new-body',
@@ -1742,38 +2200,48 @@ describe('modules/platform/gitea/index', () => {
         once: false,
       });
 
-      expect(logger.warn).toHaveBeenCalledTimes(1);
+      expect(logger.warn).toHaveBeenCalledWith(
+        { err: expect.any(Error) },
+        'Could not ensure issue',
+      );
     });
 
     it('should return null for disabled issues', async () => {
-      await initFakeRepo({ has_issues: false });
-      expect(
-        await gitea.ensureIssue({
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope, { has_issues: false });
+
+      await expect(
+        gitea.ensureIssue({
           title: 'new-title',
           body: 'new-body',
           shouldReOpen: false,
           once: false,
         }),
-      ).toBeNull();
+      ).resolves.toBeNull();
     });
   });
 
   describe('ensureIssueClosing', () => {
     it('should close issues with matching title', async () => {
       const mockIssue = mockIssues[0];
-      helper.searchIssues.mockResolvedValueOnce(mockIssues);
-      await initFakeRepo();
-      await gitea.ensureIssueClosing(mockIssue.title);
-
-      expect(helper.closeIssue).toHaveBeenCalledTimes(1);
-      expect(helper.closeIssue).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        mockIssue.number,
-      );
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues')
+        .query({ state: 'all', type: 'issues' })
+        .reply(200, mockIssues)
+        .patch('/repos/some/repo/issues/1', { state: 'closed' })
+        .reply(200, { ...mockIssue, state: 'closed' });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(gitea.ensureIssueClosing(mockIssue.title)).toResolve();
     });
 
     it('should return for disabled issues', async () => {
-      await initFakeRepo({ has_issues: false });
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope, { has_issues: false });
       await expect(gitea.ensureIssueClosing('new-title')).toResolve();
     });
   });
@@ -1781,27 +2249,36 @@ describe('modules/platform/gitea/index', () => {
   describe('deleteLabel', () => {
     it('should delete a label which exists', async () => {
       const mockLabel = mockRepoLabels[0];
-      helper.getRepoLabels.mockResolvedValueOnce(mockRepoLabels);
-      helper.getOrgLabels.mockRejectedValueOnce(new Error());
-      await initFakeRepo();
-      await gitea.deleteLabel(42, mockLabel.name);
-
-      expect(helper.unassignLabel).toHaveBeenCalledTimes(1);
-      expect(helper.unassignLabel).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        42,
-        mockLabel.id,
-      );
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/orgs/some/labels')
+        .replyWithError('unknown')
+        .get('/repos/some/repo/labels')
+        .reply(200, mockRepoLabels)
+        .delete(`/repos/some/repo/issues/42/labels/${mockLabel.id}`)
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(gitea.deleteLabel(42, mockLabel.name)).toResolve();
     });
 
     it('should gracefully fail with warning if label is missing', async () => {
-      helper.getRepoLabels.mockResolvedValueOnce(mockRepoLabels);
-      helper.getOrgLabels.mockResolvedValueOnce([]);
-      await initFakeRepo();
-      await gitea.deleteLabel(42, 'missing');
-
-      expect(helper.unassignLabel).not.toHaveBeenCalled();
-      expect(logger.warn).toHaveBeenCalledTimes(1);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/orgs/some/labels')
+        .reply(200, [])
+        .get('/repos/some/repo/labels')
+        .reply(200, mockRepoLabels);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(gitea.deleteLabel(42, 'missing')).toResolve();
+
+      expect(logger.warn).toHaveBeenCalledWith(
+        { issue: 42, labelName: 'missing' },
+        'Failed to lookup label for deletion',
+      );
     });
   });
 
@@ -1813,87 +2290,91 @@ describe('modules/platform/gitea/index', () => {
 
   describe('ensureComment', () => {
     it('should add comment with topic if not found', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      helper.createComment.mockResolvedValueOnce(partial<Comment>({ id: 42 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .post('/repos/some/repo/issues/1/comments', {
+          body: '### other-topic\n\nother-content',
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureComment({
         number: 1,
         topic: 'other-topic',
         content: 'other-content',
       });
-      const body = '### other-topic\n\nother-content';
 
-      expect(res).toBe(true);
-      expect(helper.updateComment).not.toHaveBeenCalled();
-      expect(helper.createComment).toHaveBeenCalledTimes(1);
-      expect(helper.createComment).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        1,
-        body,
-      );
+      expect(res).toBeTrue();
     });
 
     it('should add comment without topic if not found', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      helper.createComment.mockResolvedValueOnce(partial<Comment>({ id: 42 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .post('/repos/some/repo/issues/1/comments', { body: 'other-content' })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureComment({
         number: 1,
         content: 'other-content',
         topic: null,
       });
 
-      expect(res).toBe(true);
-      expect(helper.updateComment).not.toHaveBeenCalled();
-      expect(helper.createComment).toHaveBeenCalledTimes(1);
-      expect(helper.createComment).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        1,
-        'other-content',
-      );
+      expect(res).toBeTrue();
     });
 
     it('should update comment with topic if found', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      helper.updateComment.mockResolvedValueOnce(partial<Comment>({ id: 13 }));
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .patch('/repos/some/repo/issues/comments/13', {
+          body: '### some-topic\n\nsome-new-content',
+        })
+        .reply(200, partial<Comment>({ id: 13 }));
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      await initFakeRepo();
       const res = await gitea.ensureComment({
         number: 1,
         topic: 'some-topic',
         content: 'some-new-content',
       });
-      const body = '### some-topic\n\nsome-new-content';
 
-      expect(res).toBe(true);
-      expect(helper.createComment).not.toHaveBeenCalled();
-      expect(helper.updateComment).toHaveBeenCalledTimes(1);
-      expect(helper.updateComment).toHaveBeenCalledWith(
-        mockRepo.full_name,
-        13,
-        body,
-      );
+      expect(res).toBeTrue();
     });
 
     it('should skip if comment is up-to-date', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       const res = await gitea.ensureComment({
         number: 1,
         topic: 'some-topic',
         content: 'some-content',
       });
 
-      expect(res).toBe(true);
-      expect(helper.createComment).not.toHaveBeenCalled();
-      expect(helper.updateComment).not.toHaveBeenCalled();
+      expect(res).toBeTrue();
     });
 
     it('should gracefully fail with warning', async () => {
-      helper.getComments.mockRejectedValueOnce(new Error());
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       const res = await gitea.ensureComment({
         number: 1,
         topic: 'some-topic',
@@ -1901,78 +2382,119 @@ describe('modules/platform/gitea/index', () => {
       });
 
       expect(res).toBe(false);
-      expect(logger.warn).toHaveBeenCalledTimes(1);
+      expect(logger.warn).toHaveBeenCalledWith(
+        { err: expect.any(Error), issue: 1, subject: 'some-topic' },
+        'Error ensuring comment',
+      );
     });
   });
 
   describe('ensureCommentRemoval', () => {
     it('should remove existing comment by topic', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      await initFakeRepo();
-      await gitea.ensureCommentRemoval({
-        type: 'by-topic',
-        number: 1,
-        topic: 'some-topic',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .delete('/repos/some/repo/issues/comments/13')
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.deleteComment).toHaveBeenCalledTimes(1);
-      expect(helper.deleteComment).toHaveBeenCalledWith(mockRepo.full_name, 13);
+      await expect(
+        gitea.ensureCommentRemoval({
+          type: 'by-topic',
+          number: 1,
+          topic: 'some-topic',
+        }),
+      ).toResolve();
     });
 
     it('should remove existing comment by content', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      await initFakeRepo();
-      await gitea.ensureCommentRemoval({
-        type: 'by-content',
-        number: 1,
-        content: 'some-body',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .delete('/repos/some/repo/issues/comments/11')
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.deleteComment).toHaveBeenCalledTimes(1);
-      expect(helper.deleteComment).toHaveBeenCalledWith(mockRepo.full_name, 11);
+      await expect(
+        gitea.ensureCommentRemoval({
+          type: 'by-content',
+          number: 1,
+          content: 'some-body',
+        }),
+      ).toResolve();
     });
 
     it('should gracefully fail with warning', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      helper.deleteComment.mockRejectedValueOnce(new Error());
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments)
+        .delete('/repos/some/repo/issues/comments/13')
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       await gitea.ensureCommentRemoval({
         type: 'by-topic',
         number: 1,
         topic: 'some-topic',
       });
 
-      expect(logger.warn).toHaveBeenCalledTimes(1);
+      expect(logger.warn).toHaveBeenCalledWith(
+        {
+          config: { number: 1, topic: 'some-topic', type: 'by-topic' },
+          err: expect.any(Error),
+          issue: 1,
+        },
+        'Error deleting comment',
+      );
     });
 
     it('should abort silently if comment is missing', async () => {
-      helper.getComments.mockResolvedValueOnce(mockComments);
-      await initFakeRepo();
-      await gitea.ensureCommentRemoval({
-        type: 'by-topic',
-        number: 1,
-        topic: 'missing',
-      });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/issues/1/comments')
+        .reply(200, mockComments);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.deleteComment).not.toHaveBeenCalled();
+      await expect(
+        gitea.ensureCommentRemoval({
+          type: 'by-topic',
+          number: 1,
+          topic: 'missing',
+        }),
+      ).toResolve();
     });
   });
 
   describe('getBranchPr', () => {
     it('should return existing pull request for branch', async () => {
-      const mockPR = mockPRs[0];
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(await gitea.getBranchPr(mockPR.head.label)).toHaveProperty(
-        'number',
-        mockPR.number,
-      );
+      const res = await gitea.getBranchPr('some-head-branch');
+
+      expect(res).toMatchObject({ number: 1 });
     });
 
     it('should return null if no pull request exists', async () => {
-      helper.searchPRs.mockResolvedValueOnce(mockPRs);
-      await initFakeRepo();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/pulls')
+        .query({ state: 'all' })
+        .reply(200, mockPRs);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
       expect(await gitea.getBranchPr('missing')).toBeNull();
     });
@@ -1980,49 +2502,58 @@ describe('modules/platform/gitea/index', () => {
 
   describe('addAssignees', () => {
     it('should add assignees to the issue', async () => {
-      await initFakeRepo();
-      await gitea.addAssignees(1, ['me', 'you']);
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .patch('/repos/some/repo/issues/1', {
+          assignees: ['me', 'you'],
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
 
-      expect(helper.updateIssue).toHaveBeenCalledTimes(1);
-      expect(helper.updateIssue).toHaveBeenCalledWith(mockRepo.full_name, 1, {
-        assignees: ['me', 'you'],
-      });
+      await expect(gitea.addAssignees(1, ['me', 'you'])).toResolve();
     });
   });
 
   describe('addReviewers', () => {
     it('should assign reviewers', async () => {
-      expect.assertions(3);
-      await initFakePlatform();
-      const mockPR = mockPRs[0];
-      await expect(
-        gitea.addReviewers(mockPR.number, ['me', 'you']),
-      ).resolves.not.toThrow();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls/1/requested_reviewers', {
+          reviewers: ['me', 'you'],
+        })
+        .reply(200);
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
+      await expect(gitea.addReviewers(1, ['me', 'you'])).toResolve();
 
-      expect(helper.requestPrReviewers).toHaveBeenCalledTimes(1);
       expect(logger.warn).not.toHaveBeenCalled();
     });
 
-    it('should should do nothing if version to old', async () => {
-      expect.assertions(3);
-      const mockPR = mockPRs[0];
-      await expect(
-        gitea.addReviewers(mockPR.number, ['me', 'you']),
-      ).resolves.not.toThrow();
+    it('should do nothing for older Gitea versions', async () => {
+      const scope = httpMock.scope('https://gitea.com/api/v1');
+      await initFakePlatform(scope, '1.10.0');
+      await initFakeRepo(scope);
 
-      expect(helper.requestPrReviewers).not.toHaveBeenCalled();
-      expect(logger.warn).not.toHaveBeenCalled();
+      await expect(gitea.addReviewers(1, ['me', 'you'])).toResolve();
     });
 
     it('catches errors', async () => {
-      expect.assertions(2);
-      const mockPR = mockPRs[0];
-      await initFakePlatform();
-      helper.requestPrReviewers.mockRejectedValueOnce(null);
-      await expect(
-        gitea.addReviewers(mockPR.number, ['me', 'you']),
-      ).resolves.not.toThrow();
-      expect(logger.warn).toHaveBeenCalled();
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .post('/repos/some/repo/pulls/1/requested_reviewers', {
+          reviewers: ['me', 'you'],
+        })
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+      ///
+      await expect(gitea.addReviewers(1, ['me', 'you'])).toResolve();
+      expect(logger.warn).toHaveBeenCalledWith(
+        { err: expect.any(Error), number: 1, reviewers: ['me', 'you'] },
+        'Failed to assign reviewer',
+      );
     });
   });
 
@@ -2040,34 +2571,49 @@ describe('modules/platform/gitea/index', () => {
   describe('getJsonFile()', () => {
     it('returns file content', async () => {
       const data = { foo: 'bar' };
-      helper.getRepoContents.mockResolvedValueOnce({
-        contentString: JSON.stringify(data),
-        path: 'path',
-      });
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json')
+        .reply(200, {
+          content: Buffer.from(JSON.stringify(data), 'utf-8'),
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       const res = await gitea.getJsonFile('file.json');
+
       expect(res).toEqual(data);
     });
 
     it('returns file content from given repo', async () => {
       const data = { foo: 'bar' };
-      helper.getRepoContents.mockResolvedValueOnce({
-        contentString: JSON.stringify(data),
-        path: 'path',
-      });
-      await initFakeRepo({ full_name: 'different/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/different/repo/contents/file.json')
+        .reply(200, {
+          content: Buffer.from(JSON.stringify(data), 'utf-8'),
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope, { full_name: 'different/repo' });
+
       const res = await gitea.getJsonFile('file.json', 'different/repo');
+
       expect(res).toEqual(data);
     });
 
     it('returns file content from branch or tag', async () => {
       const data = { foo: 'bar' };
-      helper.getRepoContents.mockResolvedValueOnce({
-        contentString: JSON.stringify(data),
-        path: 'path',
-      });
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json?ref=dev')
+        .reply(200, {
+          content: Buffer.from(JSON.stringify(data), 'utf-8'),
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       const res = await gitea.getJsonFile('file.json', 'some/repo', 'dev');
+
       expect(res).toEqual(data);
     });
 
@@ -2078,33 +2624,49 @@ describe('modules/platform/gitea/index', () => {
           foo: 'bar'
         }
       `;
-      helper.getRepoContents.mockResolvedValueOnce({
-        contentString: json5Data,
-        path: 'path',
-      });
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json5')
+        .reply(200, {
+          content: Buffer.from(json5Data, 'utf-8'),
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
+
       const res = await gitea.getJsonFile('file.json5');
+
       expect(res).toEqual({ foo: 'bar' });
     });
 
     it('throws on malformed JSON', async () => {
-      helper.getRepoContents.mockResolvedValueOnce({
-        contentString: '!@#',
-        path: 'path',
-      });
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json')
+        .reply(200, {
+          content: Buffer.from('!@#', 'utf-8'),
+        });
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
       await expect(gitea.getJsonFile('file.json')).rejects.toThrow();
     });
 
     it('returns null on missing content', async () => {
-      helper.getRepoContents.mockResolvedValueOnce(partial<RepoContents>());
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json')
+        .reply(200, {});
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
       expect(await gitea.getJsonFile('file.json')).toBeNull();
     });
 
     it('throws on errors', async () => {
-      helper.getRepoContents.mockRejectedValueOnce(new Error('some error'));
-      await initFakeRepo({ full_name: 'some/repo' });
+      const scope = httpMock
+        .scope('https://gitea.com/api/v1')
+        .get('/repos/some/repo/contents/file.json')
+        .replyWithError('unknown');
+      await initFakePlatform(scope);
+      await initFakeRepo(scope);
       await expect(gitea.getJsonFile('file.json')).rejects.toThrow();
     });
   });
diff --git a/lib/modules/platform/gitea/index.ts b/lib/modules/platform/gitea/index.ts
index f26814f268a2674939fb4ba452deec20a8feeed8..de066724ccc77487a625380a0f9855874444c51a 100644
--- a/lib/modules/platform/gitea/index.ts
+++ b/lib/modules/platform/gitea/index.ts
@@ -430,7 +430,10 @@ const platform: Platform = {
       return 'yellow';
     }
 
-    return helper.giteaToRenovateStatusMapping[ccs.worstStatus] ?? 'yellow';
+    return (
+      helper.giteaToRenovateStatusMapping[ccs.worstStatus] ??
+      /* istanbul ignore next */ 'yellow'
+    );
   },
 
   async getBranchStatusCheck(