Skip to content
Snippets Groups Projects
Select Git revision
  • c4ec53cac4b8f45a79d91795d42adc167fb335ec
  • master default protected
  • gh-pages
  • dependabot/npm_and_yarn/nock-14.0.6
  • dependabot/npm_and_yarn/react-19.1.0
  • dependabot/npm_and_yarn/react-dom-19.1.0
  • server-2025-02-01-6100669a
  • server-2024-11-01-87cba042
  • server-2024-10-01-6875b7c8
  • dependabot/npm_and_yarn/path-to-regexp-8.2.0
  • server-2024-09-01-3d52575c
  • daily-tests-gha2
  • daily-tests-gha
  • server-2023-12-01-92d8fb8e
  • server-2023-11-01-a80c93fd
  • server-2023-10-01-31096085
  • coc-v2
  • server-2023-09-01-8edc3810
  • server-2023-08-01-75858a03
  • server-2023-07-01-02183d8d
  • test-9317
  • server-2025-07-01
  • 5.0.2
  • 5.0.1
  • 5.0.0
  • server-2025-06-01
  • server-2025-05-01
  • server-2025-04-03
  • server-2025-03-02
  • server-2025-03-01
  • server-2025-02-02
  • server-2025-01-01
  • server-2024-12-01
  • server-2024-11-02
  • 4.1.0
  • server-2024-09-25
  • server-2024-09-02
  • server-2024-08-01
  • server-2024-07-01
  • 4.0.0
  • server-2024-06-01
41 results

update-github-api.js

Blame
  • master-issue.spec.ts 14.76 KiB
    import fs from 'fs';
    import { mock } from 'jest-mock-extended';
    import { PR_STATE_NOT_OPEN } from '../../constants/pull-requests';
    import * as masterIssue from './master-issue';
    import { RenovateConfig, getConfig, platform } from '../../../test/util';
    import { BranchConfig, BranchUpgradeConfig } from '../common';
    import { Pr } from '../../platform';
    import { PLATFORM_TYPE_GITHUB } from '../../constants/platforms';
    
    type PrUpgrade = BranchUpgradeConfig;
    
    let config: RenovateConfig;
    beforeEach(() => {
      jest.resetAllMocks();
      config = getConfig();
      config.platform = PLATFORM_TYPE_GITHUB;
      config.errors = [];
      config.warnings = [];
    });
    
    async function dryRun(
      branches: BranchConfig[],
      // eslint-disable-next-line no-shadow
      platform,
      ensureIssueClosingCalls = 0,
      ensureIssueCalls = 0,
      getBranchPrCalls = 0,
      findPrCalls = 0
    ) {
      jest.resetAllMocks();
      config.dryRun = true;
      await masterIssue.ensureMasterIssue(config, branches);
      expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(
        ensureIssueClosingCalls
      );
      expect(platform.ensureIssue).toHaveBeenCalledTimes(ensureIssueCalls);
      expect(platform.getBranchPr).toHaveBeenCalledTimes(getBranchPrCalls);
      expect(platform.findPr).toHaveBeenCalledTimes(findPrCalls);
    }
    
    describe('workers/repository/master-issue', () => {
      describe('ensureMasterIssue()', () => {
        it('do nothing if masterissue is disable', async () => {
          const branches: BranchConfig[] = [];
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(0);
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('do nothing if it has no masterissueapproval branches', async () => {
          const branches = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              masterIssueApproval: false,
            },
          ];
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(0);
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('closes master issue when there is 0 PR opened and masterIssueAutoclose is true', async () => {
          const branches: BranchConfig[] = [];
          config.masterIssue = true;
          config.masterIssueAutoclose = true;
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssueClosing.mock.calls[0][0]).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue).toHaveBeenCalledTimes(0);
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('closes master issue when all branches are automerged and masterIssueAutoclose is true', async () => {
          const branches: BranchConfig[] = [
            { ...mock<BranchConfig>(), prTitle: 'pr1', res: 'automerged' },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              res: 'automerged',
              masterIssueApproval: false,
            },
          ];
          config.masterIssue = true;
          config.masterIssueAutoclose = true;
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssueClosing.mock.calls[0][0]).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue).toHaveBeenCalledTimes(0);
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('open or update master issue when all branches are closed and masterIssueAutoclose is false', async () => {
          const branches: BranchConfig[] = [];
          config.masterIssue = true;
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            'This repository is up-to-date and has no outstanding updates open or pending.'
          );
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('checks an issue with 2 Pending Approvals, 2 not scheduled, 2 pr-hourly-limit-reached and 2 in error', async () => {
          const branches: BranchConfig[] = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
              upgrades: [{ ...mock<BranchUpgradeConfig>(), depName: 'dep1' }],
              res: 'needs-approval',
              branchName: 'branchName1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep2' }],
              res: 'needs-approval',
              branchName: 'branchName2',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr3',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep3' }],
              res: 'not-scheduled',
              branchName: 'branchName3',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr4',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep4' }],
              res: 'not-scheduled',
              branchName: 'branchName4',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr5',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep5' }],
              res: 'pr-hourly-limit-reached',
              branchName: 'branchName5',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr6',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep6' }],
              res: 'pr-hourly-limit-reached',
              branchName: 'branchName6',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr7',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep7' }],
              res: 'error',
              branchName: 'branchName7',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr8',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep8' }],
              res: 'error',
              branchName: 'branchName8',
            },
          ];
          config.masterIssue = true;
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            fs.readFileSync(
              'lib/workers/repository/__fixtures__/master-issue_with_8_PR.txt',
              'utf8'
            )
          );
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
    
        it('checks an issue with 2 PR pr-edited', async () => {
          const branches: BranchConfig[] = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep1' }],
              res: 'pr-edited',
              branchName: 'branchName1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              upgrades: [
                { ...mock<PrUpgrade>(), depName: 'dep2' },
                { ...mock<PrUpgrade>(), depName: 'dep3' },
              ],
              res: 'pr-edited',
              branchName: 'branchName2',
            },
          ];
          config.masterIssue = true;
          platform.getBranchPr
            .mockResolvedValueOnce({ ...mock<Pr>(), number: 1 })
            .mockResolvedValueOnce(undefined);
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            fs.readFileSync(
              'lib/workers/repository/__fixtures__/master-issue_with_2_PR_edited.txt',
              'utf8'
            )
          );
          expect(platform.getBranchPr).toHaveBeenCalledTimes(2);
          expect(platform.getBranchPr.mock.calls[0][0]).toBe('branchName1');
          expect(platform.getBranchPr.mock.calls[1][0]).toBe('branchName2');
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform, 0, 0, 2, 0);
        });
    
        it('checks an issue with 3 PR in progress and rebase all option', async () => {
          const branches: BranchConfig[] = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep1' }],
              res: 'rebase',
              branchName: 'branchName1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              upgrades: [
                { ...mock<PrUpgrade>(), depName: 'dep2' },
                { ...mock<PrUpgrade>(), depName: 'dep3' },
              ],
              res: 'rebase',
              branchName: 'branchName2',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr3',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep3' }],
              res: 'rebase',
              branchName: 'branchName3',
            },
          ];
          config.masterIssue = true;
          platform.getBranchPr
            .mockResolvedValueOnce({ ...mock<Pr>(), number: 1 })
            .mockResolvedValueOnce(undefined)
            .mockResolvedValueOnce({ ...mock<Pr>(), number: 3 });
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            fs.readFileSync(
              'lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_progress.txt',
              'utf8'
            )
          );
          expect(platform.getBranchPr).toHaveBeenCalledTimes(3);
          expect(platform.getBranchPr.mock.calls[0][0]).toBe('branchName1');
          expect(platform.getBranchPr.mock.calls[1][0]).toBe('branchName2');
          expect(platform.getBranchPr.mock.calls[2][0]).toBe('branchName3');
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform, 0, 0, 3, 0);
        });
    
        it('checks an issue with 2 PR closed / ignored', async () => {
          const branches: BranchConfig[] = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep1' }],
              res: 'already-existed',
              branchName: 'branchName1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              upgrades: [
                { ...mock<PrUpgrade>(), depName: 'dep2' },
                { ...mock<PrUpgrade>(), depName: 'dep3' },
              ],
              res: 'already-existed',
              branchName: 'branchName2',
            },
          ];
          config.masterIssue = true;
          platform.getBranchPr
            .mockResolvedValueOnce({ ...mock<Pr>(), number: 1 })
            .mockResolvedValueOnce(undefined)
            .mockResolvedValueOnce({ ...mock<Pr>(), number: 3 });
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            fs.readFileSync(
              'lib/workers/repository/__fixtures__/master-issue_with_2_PR_closed_ignored.txt',
              'utf8'
            )
          );
          expect(platform.getBranchPr).toHaveBeenCalledTimes(0);
          expect(platform.findPr).toHaveBeenCalledTimes(2);
          expect(platform.findPr.mock.calls[0][0].branchName).toBe('branchName1');
          expect(platform.findPr.mock.calls[0][0].prTitle).toBe('pr1');
          expect(platform.findPr.mock.calls[0][0].state).toBe(PR_STATE_NOT_OPEN);
          expect(platform.findPr.mock.calls[1][0].branchName).toBe('branchName2');
          expect(platform.findPr.mock.calls[1][0].prTitle).toBe('pr2');
          expect(platform.findPr.mock.calls[1][0].state).toBe(PR_STATE_NOT_OPEN);
    
          // same with dry run
          await dryRun(branches, platform, 0, 0, 0, 2);
        });
    
        it('checks an issue with 3 PR in approval', async () => {
          const branches: BranchConfig[] = [
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr1',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep1' }],
              res: 'needs-pr-approval',
              branchName: 'branchName1',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr2',
              upgrades: [
                { ...mock<PrUpgrade>(), depName: 'dep2' },
                { ...mock<PrUpgrade>(), depName: 'dep3' },
              ],
              res: 'needs-pr-approval',
              branchName: 'branchName2',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr3',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep3' }],
              res: 'needs-pr-approval',
              branchName: 'branchName3',
            },
            {
              ...mock<BranchConfig>(),
              prTitle: 'pr4',
              upgrades: [{ ...mock<PrUpgrade>(), depName: 'dep4' }],
              res: 'pending',
              branchName: 'branchName4',
            },
          ];
          config.masterIssue = true;
          config.masterIssuePrApproval = true;
          await masterIssue.ensureMasterIssue(config, branches);
          expect(platform.ensureIssueClosing).toHaveBeenCalledTimes(0);
          expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
          expect(platform.ensureIssue.mock.calls[0][0].title).toBe(
            config.masterIssueTitle
          );
          expect(platform.ensureIssue.mock.calls[0][0].body).toBe(
            fs.readFileSync(
              'lib/workers/repository/__fixtures__/master-issue_with_3_PR_in_approval.txt',
              'utf8'
            )
          );
          expect(platform.findPr).toHaveBeenCalledTimes(0);
    
          // same with dry run
          await dryRun(branches, platform);
        });
      });
    });