Files
renovate/lib/modules/platform/forgejo/pr-cache.spec.ts
Sebastian Poxhofer 7573fa98f2 feat(platform/forgejo): validate http request with schemas (#43900)
* fix(platform/forgejo): replace getJsonUnchecked with validated getJson

Migrate all getJsonUnchecked calls in the Forgejo platform module to use
getJson with Zod schemas for response validation. Add comprehensive Zod
schemas to schema.ts and update test mocks to use proper data formats.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* simplify by using infered types

* first pass on reducing duplications

* move PrStatus and CommitStatus to schema

* remove code handling BranchStatus values now ensured by schema

* remove now unreachable code

* fix coverage

* use forgejo schemas instead gitea for changelog fetching

* feat: do not abort repo on unrecognized default merge style

* refactor: instead of usages of `?? {}` with a default parameter

* tighten required parameters and move contentString generation to schema

* refactor: merge both content schemas and use a discriminated union

* refactor: move patchJson and postJson to schema validation

* Make body field required

* make schema more strict

* use DeepNullish

* simplify catch

* revert the gitea Content type change

* unify User

* log more nicely if accidentally not a file has been referenced

* Use fakeSha and LongCommitSha schema

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 14:22:01 +00:00

214 lines
5.1 KiB
TypeScript

import { fakeSha, partial } from '~test/util.ts';
import * as httpMock from '../../../../test/http-mock.ts';
import { reset as memCacheReset } from '../../../util/cache/memory/index.ts';
import {
getCache,
resetCache as repoCacheReset,
} from '../../../util/cache/repository/index.ts';
import { ForgejoHttp, setBaseUrl } from '../../../util/http/forgejo.ts';
import { ForgejoPrCache } from './pr-cache.ts';
import type { PR, Repo } from './schema.ts';
import { toRenovatePR } from './utils.ts';
const http = new ForgejoHttp();
const ignorePrAuthor = false;
const baseUrl = 'https://code.forgejo.org/api/v1';
setBaseUrl('https://code.forgejo.org');
const pr1: PR = {
number: 1,
title: 'title',
body: 'other random pull request',
state: 'open',
diff_url: 'https://forgejo.renovatebot.com/some/repo/pulls/3.diff',
created_at: '2011-08-18T22:30:38Z',
updated_at: '2011-08-18T22:30:38Z',
closed_at: undefined,
mergeable: true,
base: { ref: 'third-party-base-branch' },
head: {
label: 'other-head-branch',
sha: fakeSha('other-head-sha'),
repo: partial<Repo>({ full_name: 'SOME/repo' }),
},
user: { id: 1, login: 'some-author' },
};
const pr2: PR = {
number: 2,
title: 'title',
body: 'other random pull request',
state: 'open',
diff_url: 'https://forgejo.renovatebot.com/some/repo/pulls/3.diff',
created_at: '2011-08-18T22:30:38Z',
updated_at: '2011-08-18T22:30:38Z',
closed_at: undefined,
mergeable: true,
base: { ref: 'third-party-base-branch' },
head: {
label: 'other-head-branch',
sha: fakeSha('other-head-sha'),
repo: partial<Repo>({ full_name: 'SOME/repo' }),
},
user: { id: 1, login: 'some-author' },
};
describe('modules/platform/forgejo/pr-cache', () => {
let cache = getCache();
beforeEach(() => {
memCacheReset();
repoCacheReset();
cache = getCache();
});
it('fetches cache - author defined', async () => {
httpMock
.scope(baseUrl)
.get(
'/repos/SOME/repo/pulls?state=all&sort=recentupdate&limit=100&poster=some-author',
)
.reply(200, [pr1], {
Link: '<https://stash.renovatebot.com/repos/SOME/repo/pulls?state=all&sort=recentupdate&limit=50&page=2>; rel="next"',
})
.get(
'/repos/SOME/repo/pulls?&state=all&sort=recentupdate&limit=50&page=2',
)
.reply(200, [pr2]);
const res = await ForgejoPrCache.getPrs(
http,
'SOME/repo',
ignorePrAuthor,
'some-author',
);
expect(res).toMatchObject([
{
number: 2,
title: 'title',
},
{
number: 1,
title: 'title',
},
]);
expect(cache).toEqual({
platform: {
forgejo: {
pullRequestsCache: {
author: 'some-author',
items: {
'1': toRenovatePR(pr1, 'some-author'),
'2': toRenovatePR(pr2, 'some-author'),
},
updated_at: '2011-08-18T22:30:38Z',
},
},
},
});
});
it('resets cache for not matching authors', async () => {
cache.platform = {
forgejo: {
pullRequestsCache: {
items: {
'1': toRenovatePR(pr1, 'some-other-author'),
},
author: 'some-other-author',
updated_at: '2011-08-18T22:30:38Z',
},
},
};
httpMock
.scope(baseUrl)
.get(
'/repos/SOME/repo/pulls?state=all&sort=recentupdate&limit=100&poster=some-author',
)
.reply(200, [pr1]);
const res = await ForgejoPrCache.getPrs(
http,
'SOME/repo',
ignorePrAuthor,
'some-author',
);
expect(res).toMatchObject([
{
number: 1,
title: 'title',
},
]);
expect(cache).toEqual({
platform: {
forgejo: {
pullRequestsCache: {
author: 'some-author',
items: {
'1': toRenovatePR(pr1, 'some-author'),
},
updated_at: '2011-08-18T22:30:38Z',
},
},
},
});
});
it('syncs cache', async () => {
cache.platform = {
forgejo: {
pullRequestsCache: {
items: {
'1': toRenovatePR(pr1, 'some-author'),
},
author: 'some-author',
updated_at: '2011-08-18T22:30:38Z',
},
},
};
httpMock
.scope(baseUrl)
.get(
'/repos/SOME/repo/pulls?state=all&sort=recentupdate&limit=20&poster=some-author',
)
.reply(200, [pr2, pr1]);
const res = await ForgejoPrCache.getPrs(
http,
'SOME/repo',
ignorePrAuthor,
'some-author',
);
expect(res).toMatchObject([
{
number: 2,
title: 'title',
},
{
number: 1,
title: 'title',
},
]);
expect(cache).toEqual({
platform: {
forgejo: {
pullRequestsCache: {
author: 'some-author',
items: {
'1': toRenovatePR(pr1, 'some-author'),
'2': toRenovatePR(pr2, 'some-author'),
},
updated_at: '2011-08-18T22:30:38Z',
},
},
},
});
});
});