import type { SimpleGit } from 'simple-git'; import type { MockProxy } from 'vitest-mock-extended'; import { mock } from 'vitest-mock-extended'; import { Fixtures } from '~test/fixtures.ts'; import { clearEnv } from '~test/util.ts'; import * as git from '../../../util/git/index.ts'; import { getPkgReleases } from '../index.ts'; import { GitRefsDatasource } from './index.ts'; const createSimpleGit = vi.mocked(git.createSimpleGit); const packageName = 'https://github.com/example/example.git'; const lsRemote1 = Fixtures.get('ls-remote-1.txt'); const datasource = GitRefsDatasource.id; describe('modules/datasource/git-refs/index', () => { let gitMock: MockProxy; beforeEach(() => { clearEnv(); // reset git mock gitMock = mock({ env: vi.fn(), listRemote: vi.fn(), }); createSimpleGit.mockReturnValue(gitMock); }); describe('getReleases', () => { it('returns nil if response is wrong', async () => { gitMock.listRemote.mockResolvedValue(''); const versions = await getPkgReleases({ datasource, packageName, }); expect(versions).toBeNull(); }); it('returns nil if response is malformed', async () => { gitMock.listRemote.mockResolvedValue('aabbccddeeff'); const { releases } = (await getPkgReleases({ datasource, packageName, }))!; expect(releases).toBeEmpty(); }); it('returns nil if remote call throws exception', async () => { gitMock.listRemote.mockRejectedValue(new Error()); const versions = await getPkgReleases({ datasource, packageName, }); expect(versions).toBeNull(); }); it('returns versions filtered from tags', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const versions = await getPkgReleases({ datasource, packageName, }); expect(versions).toEqual({ releases: [ { gitRef: 'v1.0.0', newDigest: '7b756026fb2de270240a889a413e7e3a9d4d4d85', version: 'v1.0.0', }, { gitRef: 'v1.0.1', newDigest: 'e173183f932ba8a31d0e4f23cc1070e8ebfa59d6', version: 'v1.0.1', }, { gitRef: 'v1.0.2', newDigest: '3936a6bced3587dc9fd464b0a910e0dfd4cfe10d', version: 'v1.0.2', }, { gitRef: 'v1.0.3', newDigest: '125ca9f3df4151e50046e5327ecb29ec4c13efab', version: 'v1.0.3', }, { gitRef: 'v1.0.4', newDigest: '3ed9e7d7094fd4ee7751c24a3e6b706060f461ff', version: 'v1.0.4', }, { gitRef: 'v1.0.5', newDigest: '6d7a933c2e6b7b39e992b1f93b6b42de083b28f0', version: 'v1.0.5', }, ], sourceUrl: 'https://github.com/example/example', }); const result = versions?.releases.map((x) => x.version).sort(); expect(result).toHaveLength(6); }); }); describe('getDigest()', () => { it('returns null if not found', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const digest = await new GitRefsDatasource().getDigest( { packageName: 'a tag to look up' }, 'v2.0.0', ); expect(digest).toBeNull(); }); it('returns digest for tag', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const digest = await new GitRefsDatasource().getDigest( { packageName: 'a tag to look up' }, 'v1.0.4', ); // For annotated tags, we return the dereferenced commit hash (^{}) // to match what `git submodule status` returns expect(digest).toBe('3ed9e7d7094fd4ee7751c24a3e6b706060f461ff'); }); it('ignores refs/for/', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const digest = await new GitRefsDatasource().getDigest( { packageName: 'a tag to look up' }, 'master', ); expect(digest).toBe('a9920c014aebc28dc1b23e7efcc006d0455cc710'); }); it('returns digest for HEAD', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const digest = await new GitRefsDatasource().getDigest( { packageName: 'another tag to look up' }, undefined, ); expect(digest).toBe('a9920c014aebc28dc1b23e7efcc006d0455cc710'); }); it('requests authentication for git-refs lookups', async () => { gitMock.listRemote.mockResolvedValue(lsRemote1); const digest = await new GitRefsDatasource().getDigest( { packageName: 'another tag to look up' }, undefined, ); expect(digest).toBe('a9920c014aebc28dc1b23e7efcc006d0455cc710'); expect(createSimpleGit).toHaveBeenCalledExactlyOnceWith({ authentication: { hostTypes: ['git-refs'] }, }); }); }); });