Files
Sergei Zharinov c246f814d5 feat: Add github-digest datasource (#40226)
* feat(datasource/github-digest): Add datasource for non-semver refs

* fix: Use `Promise.allSettled` to avoid dangling promises
2026-02-02 10:58:43 +00:00

233 lines
6.7 KiB
TypeScript

import * as githubGraphql from '../../../util/github/graphql/index.ts';
import * as hostRules from '../../../util/host-rules.ts';
import { asTimestamp } from '../../../util/timestamp.ts';
import { getPkgReleases } from '../index.ts';
import { GithubDigestDatasource } from './index.ts';
describe('modules/datasource/github-digest/index', () => {
const github = new GithubDigestDatasource();
beforeEach(() => {
vi.spyOn(hostRules, 'hosts').mockReturnValue([]);
vi.spyOn(hostRules, 'find').mockReturnValue({
token: 'some-token',
});
});
describe('getReleases', () => {
const packageName = 'some/repo';
it('returns tags and branches merged', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([
{
version: 'v1.0.0',
gitRef: 'v1.0.0',
releaseTimestamp: asTimestamp('2021-01-01')!,
hash: 'tag-hash-1',
},
]);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([
{
version: 'main',
gitRef: 'main',
releaseTimestamp: asTimestamp('2022-01-01')!,
hash: 'branch-hash-1',
},
{
version: 'develop',
gitRef: 'develop',
releaseTimestamp: asTimestamp('2022-02-01')!,
hash: 'branch-hash-2',
},
]);
const res = await getPkgReleases({ datasource: github.id, packageName });
expect(res).toEqual({
registryUrl: 'https://github.com',
releases: [
{
version: 'v1.0.0',
gitRef: 'v1.0.0',
releaseTimestamp: '2021-01-01T00:00:00.000Z',
newDigest: 'tag-hash-1',
},
{
version: 'main',
gitRef: 'main',
releaseTimestamp: '2022-01-01T00:00:00.000Z',
newDigest: 'branch-hash-1',
},
{
version: 'develop',
gitRef: 'develop',
releaseTimestamp: '2022-02-01T00:00:00.000Z',
newDigest: 'branch-hash-2',
},
],
sourceUrl: 'https://github.com/some/repo',
});
});
it('prioritizes tags over branches with same name', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: asTimestamp('2021-01-01')!,
hash: 'tag-v4-hash',
},
]);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: asTimestamp('2022-01-01')!,
hash: 'branch-v4-hash',
},
{
version: 'main',
gitRef: 'main',
releaseTimestamp: asTimestamp('2022-02-01')!,
hash: 'main-hash',
},
]);
const res = await getPkgReleases({ datasource: github.id, packageName });
expect(res).toEqual({
registryUrl: 'https://github.com',
releases: [
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: '2021-01-01T00:00:00.000Z',
newDigest: 'tag-v4-hash',
},
{
version: 'main',
gitRef: 'main',
releaseTimestamp: '2022-02-01T00:00:00.000Z',
newDigest: 'main-hash',
},
],
sourceUrl: 'https://github.com/some/repo',
});
});
it('returns only branches when no tags', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([]);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: asTimestamp('2022-01-01')!,
hash: 'branch-hash',
},
]);
const res = await getPkgReleases({ datasource: github.id, packageName });
expect(res).toEqual({
registryUrl: 'https://github.com',
releases: [
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: '2022-01-01T00:00:00.000Z',
newDigest: 'branch-hash',
},
],
sourceUrl: 'https://github.com/some/repo',
});
});
it('throws when tags query fails', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockRejectedValueOnce(
new Error('Tags query failed'),
);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([]);
await expect(github.getReleases({ packageName })).rejects.toThrow(
'Tags query failed',
);
});
it('throws when branches query fails', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([]);
vi.spyOn(githubGraphql, 'queryBranches').mockRejectedValueOnce(
new Error('Branches query failed'),
);
await expect(github.getReleases({ packageName })).rejects.toThrow(
'Branches query failed',
);
});
});
describe('getDigest', () => {
const packageName = 'some/repo';
it('returns tag digest when tag exists', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([
{
version: 'v1.0.0',
gitRef: 'v1.0.0',
releaseTimestamp: asTimestamp('2021-01-01')!,
hash: 'tag-hash',
},
]);
const res = await github.getDigest({ packageName }, 'v1.0.0');
expect(res).toBe('tag-hash');
});
it('returns branch digest when tag not found', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([]);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: asTimestamp('2022-01-01')!,
hash: 'branch-hash',
},
]);
const res = await github.getDigest({ packageName }, 'v4');
expect(res).toBe('branch-hash');
});
it('prefers tag over branch with same name', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([
{
version: 'v4',
gitRef: 'v4',
releaseTimestamp: asTimestamp('2021-01-01')!,
hash: 'tag-hash',
},
]);
const res = await github.getDigest({ packageName }, 'v4');
expect(res).toBe('tag-hash');
});
it('returns null when not found in tags or branches', async () => {
vi.spyOn(githubGraphql, 'queryTags').mockResolvedValueOnce([]);
vi.spyOn(githubGraphql, 'queryBranches').mockResolvedValueOnce([]);
const res = await github.getDigest({ packageName }, 'nonexistent');
expect(res).toBeNull();
});
it('returns null when newValue is undefined', async () => {
const res = await github.getDigest({ packageName }, undefined);
expect(res).toBeNull();
});
});
});