diff --git a/lib/modules/platform/github/index.spec.ts b/lib/modules/platform/github/index.spec.ts index eb133f6ecae99b..9eaf850b78dce7 100644 --- a/lib/modules/platform/github/index.spec.ts +++ b/lib/modules/platform/github/index.spec.ts @@ -1531,6 +1531,57 @@ describe('modules/platform/github/index', () => { }); }); + describe('getIssue()', () => { + it('returns null if issues disabled', async () => { + const scope = httpMock.scope(githubApiHost); + initRepoMock(scope, 'some/repo', { hasIssuesEnabled: false }); + await github.initRepo({ repository: 'some/repo' }); + const res = await github.getIssue(1); + expect(res).toBeNull(); + }); + + it('returns issue', async () => { + const scope = httpMock.scope(githubApiHost); + initRepoMock(scope, 'some/repo'); + const issue = { + number: 1, + state: 'open', + title: 'title-1', + body: 'body-1', + }; + scope + .get('/repos/some/repo/issues/1') + .reply(200, { ...issue, updated_at: '2022-01-01T00:00:00Z' }); + await github.initRepo({ repository: 'some/repo' }); + const res = await github.getIssue(1); + expect(res).toMatchObject({ + ...issue, + lastModified: '2022-01-01T00:00:00Z', + }); + }); + + it('returns null if issue not found', async () => { + const scope = httpMock.scope(githubApiHost); + initRepoMock(scope, 'some/repo'); + scope.get('/repos/some/repo/issues/1').reply(404); + await github.initRepo({ repository: 'some/repo' }); + const res = await github.getIssue(1); + expect(res).toBeNull(); + }); + + it('logs debug message if issue deleted', async () => { + const scope = httpMock.scope(githubApiHost); + initRepoMock(scope, 'some/repo'); + scope.get('/repos/some/repo/issues/1').reply(410); + await github.initRepo({ repository: 'some/repo' }); + const res = await github.getIssue(1); + expect(res).toBeNull(); + expect(logger.logger.debug).toHaveBeenCalledWith( + 'Issue #1 has been deleted', + ); + }); + }); + describe('findIssue()', () => { it('returns null if no issue', async () => { httpMock diff --git a/lib/modules/platform/github/index.ts b/lib/modules/platform/github/index.ts index 93488565ba630a..8551653ce0dac3 100644 --- a/lib/modules/platform/github/index.ts +++ b/lib/modules/platform/github/index.ts @@ -1231,7 +1231,6 @@ export async function getIssueList(): Promise { } export async function getIssue(number: number): Promise { - // istanbul ignore if if (config.hasIssuesEnabled === false) { return null; } @@ -1246,8 +1245,12 @@ export async function getIssue(number: number): Promise { ); GithubIssueCache.updateIssue(issue); return issue; - } catch (err) /* istanbul ignore next */ { + } catch (err) { logger.debug({ err, number }, 'Error getting issue'); + if (err.response?.statusCode === 410) { + logger.debug(`Issue #${number} has been deleted`); + GithubIssueCache.deleteIssue(number); + } return null; } } diff --git a/lib/modules/platform/github/issue.spec.ts b/lib/modules/platform/github/issue.spec.ts index 749364ddf5ad4e..8a9e9bd1cb82a4 100644 --- a/lib/modules/platform/github/issue.spec.ts +++ b/lib/modules/platform/github/issue.spec.ts @@ -159,6 +159,32 @@ describe('modules/platform/github/issue', () => { }); }); + it('removes particular issue from the cache', () => { + cache.platform = { + github: { + issuesCache: { + '1': { + number: 1, + body: 'body-1', + state: 'open', + title: 'title-1', + lastModified: '2020-01-01T00:00:00.000Z', + }, + }, + }, + }; + + GithubIssueCache.deleteIssue(1); + + expect(cache).toEqual({ + platform: { + github: { + issuesCache: {}, + }, + }, + }); + }); + it('reconciles cache', () => { cache.platform = { github: { diff --git a/lib/modules/platform/github/issue.ts b/lib/modules/platform/github/issue.ts index 1cc85151777233..11a5d050dceceb 100644 --- a/lib/modules/platform/github/issue.ts +++ b/lib/modules/platform/github/issue.ts @@ -85,6 +85,13 @@ export class GithubIssueCache { } } + static deleteIssue(number: number): void { + const cacheData = this.data; + if (cacheData) { + delete cacheData[number]; + } + } + /** * At the moment of repo initialization, repository cache is not available. * What we can do is to store issues for later reconciliation.