import { hostRules } from '~test/host-rules.ts'; import { partial } from '~test/util.ts'; import type { Timestamp } from '../../util/timestamp.ts'; import { GitTagsDatasource } from './git-tags/index.ts'; import { HelmDatasource } from './helm/index.ts'; import { MavenDatasource } from './maven/index.ts'; import { addMetaData, massageGithubUrl, massageGitlabUrl, massageUrl, shouldDeleteHomepage, } from './metadata.ts'; import { NpmDatasource } from './npm/index.ts'; import { PypiDatasource } from './pypi/index.ts'; import type { ReleaseResult } from './types.ts'; describe('modules/datasource/metadata', () => { it('Should handle manualChangelogUrls', () => { const dep: ReleaseResult = { releases: [ { version: '2.0.0', releaseTimestamp: '2018-07-13T10:14:17.000Z' as Timestamp, }, { version: '2.0.0.dev1', releaseTimestamp: '2017-10-24T10:09:16.000Z' as Timestamp, }, { version: '2.1.0', releaseTimestamp: '2019-01-20T19:59:28.000Z' as Timestamp, }, { version: '2.2.0', releaseTimestamp: '2019-07-16T18:29:00.000Z' as Timestamp, }, ], }; const datasource = PypiDatasource.id; const packageName = 'pycountry'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ changelogUrl: 'https://github.com/flyingcircusio/pycountry/blob/master/HISTORY.txt', sourceUrl: 'https://github.com/flyingcircusio/pycountry', }); }); it('Should handle manualSourceUrls', () => { const dep: ReleaseResult = { releases: [ { version: '2.0.0', releaseTimestamp: '2018-07-13T10:14:17.000Z' as Timestamp, }, { version: '2.0.0.dev1', releaseTimestamp: '2017-10-24T10:09:16.000Z' as Timestamp, }, { version: '2.1.0', releaseTimestamp: '2019-01-20T19:59:28.000Z' as Timestamp, }, { version: '2.2.0', releaseTimestamp: '2019-07-16T18:29:00.000Z' as Timestamp, }, ], }; const datasource = PypiDatasource.id; const packageName = 'mkdocs'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://github.com/mkdocs/mkdocs', }); }); it('Should handle parsing of sourceUrls correctly', () => { const dep: ReleaseResult = { sourceUrl: 'https://github.com/carltongibson/django-filter/tree/master', releases: [ { version: '2.0.0', releaseTimestamp: '2018-07-13T10:14:17.000Z' as Timestamp, }, { version: '2.0.0.dev1', releaseTimestamp: '2017-10-24T10:09:16.000Z' as Timestamp, }, { version: '2.1.0', releaseTimestamp: '2019-01-20T19:59:28.000Z' as Timestamp, }, { version: '2.2.0', releaseTimestamp: '2019-07-16T18:29:00.000Z' as Timestamp, }, ], }; const datasource = PypiDatasource.id; const packageName = 'django-filter'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://github.com/carltongibson/django-filter', }); }); it.each` sourceUrl | expectedSourceUrl | expectedSourceDirectory ${'https://github.com/bitnami/charts/tree/master/bitnami/kube-prometheus'} | ${'https://github.com/bitnami/charts'} | ${'bitnami/kube-prometheus'} ${'https://gitlab.com/group/sub-group/repo/tree/main/some/path'} | ${'https://gitlab.com/group/sub-group/repo'} | ${'some/path'} ${'https://gitlab.com/group/sub-group/repo/-/tree/main/some/path'} | ${'https://gitlab.com/group/sub-group/repo'} | ${'some/path'} ${'https://github.example.com/org/repo/tree/main/foo/bar/baz'} | ${'https://github.example.com/org/repo'} | ${'foo/bar/baz'} `( 'Should split the sourceDirectory out of sourceUrl for known platforms: $sourceUrl -> ($expectedSourceUrl, $expectedSourceDirectory)', ({ sourceUrl, expectedSourceUrl, expectedSourceDirectory }) => { const dep: ReleaseResult = { sourceUrl, releases: [] }; const datasource = HelmDatasource.id; const packageName = 'some-chart'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: expectedSourceUrl, sourceDirectory: expectedSourceDirectory, }); }, ); it.each` sourceUrl | expectedSourceUrl ${'git@gitlab.com:group/sub-group/repo'} | ${'https://gitlab.com/group/sub-group/repo'} ${'git@gitlab.com:group/sub-group/repo.git'} | ${'https://gitlab.com/group/sub-group/repo'} ${'git@somehost.com:group/sub-group/repo.git'} | ${'https://somehost.com/group/sub-group/repo'} `( 'Should fallback to massagedUrl for sourceUrl for non Github non HTTP(S) hosts: $sourceUrl -> $expectedSourceUrl', ({ sourceUrl, expectedSourceUrl }) => { const dep: ReleaseResult = { sourceUrl, releases: [] }; const datasource = GitTagsDatasource.id; const packageName = 'some-dep'; hostRules.add({ hostType: 'gitlab', matchHost: 'somehost.com', }); addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: expectedSourceUrl, }); }, ); it.each` sourceUrl ${'https://github.com/bitnami'} ${'https://github.com/bitnami/charts'} ${'https://gitlab.com/group'} ${'https://gitlab.com/group/repo'} ${'https://gitlab.com/group/sub-group/repo'} ${'https://github.example.com/org/repo'} ${'https://unknown-platform.com/some/repo/files/foo/bar'} `( 'Should not split a sourceDirectory when one cannot be detected $sourceUrl', ({ sourceUrl }) => { const dep: ReleaseResult = { sourceUrl, releases: [] }; const datasource = HelmDatasource.id; const packageName = 'some-chart'; addMetaData(dep, datasource, packageName); expect(dep.sourceDirectory).toBeUndefined(); expect(dep).toMatchObject({ sourceUrl }); }, ); it('Should not overwrite any existing sourceDirectory', () => { const dep: ReleaseResult = { sourceUrl: 'https://github.com/neutrinojs/neutrino/tree/master/packages/react', sourceDirectory: 'packages/foo', releases: [], }; const datasource = NpmDatasource.id; const packageName = '@neutrinojs/react'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://github.com/neutrinojs/neutrino', sourceDirectory: 'packages/foo', }); }); it('Should massage github sourceUrls', () => { const dep: ReleaseResult = { sourceUrl: 'https://some.github.com/repo', releases: [ { version: '2.0.0', releaseTimestamp: '2018-07-13T10:14:17.000Z' as Timestamp, }, { version: '2.0.0.dev1', releaseTimestamp: '2017-10-24T10:09:16.000Z' as Timestamp, }, { version: '2.1.0', releaseTimestamp: '2019-01-20T19:59:28.000Z' as Timestamp, }, { version: '2.2.0', releaseTimestamp: '2019-07-16T18:29:00.000Z' as Timestamp, }, ], }; const datasource = PypiDatasource.id; const packageName = 'django-filter'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://github.com/some/repo', }); }); it('Should handle parsing of sourceUrls correctly for GitLab also', () => { const dep: ReleaseResult = { sourceUrl: 'https://gitlab.com/meno/dropzone/tree/master', releases: [ { version: '5.7.0', releaseTimestamp: '2020-02-14T13:12:00.000Z' as Timestamp, }, { version: '5.6.1', releaseTimestamp: '2020-02-14T10:04:00.000Z' as Timestamp, }, ], }; const datasource = NpmDatasource.id; const packageName = 'dropzone'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://gitlab.com/meno/dropzone', }); }); it('Should handle failed parsing of sourceUrls for GitLab', () => { const dep = { sourceUrl: 'https://gitlab-nope', releases: [ { version: '5.7.0', releaseTimestamp: '2020-02-14T13:12:00.000Z' as Timestamp, }, { version: '5.6.1', releaseTimestamp: '2020-02-14T10:04:00.000Z' as Timestamp, }, ], }; const datasource = NpmDatasource.id; const packageName = 'dropzone'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://gitlab-nope', }); }); it('Should handle failed parsing of sourceUrls for other', () => { const dep = { sourceUrl: 'https://nope-nope-nope', releases: [ { version: '5.7.0', releaseTimestamp: '2020-02-14T13:12:00.000Z' as Timestamp, }, { version: '5.6.1', releaseTimestamp: '2020-02-14T10:04:00.000Z' as Timestamp, }, ], }; const datasource = NpmDatasource.id; const packageName = 'dropzone'; addMetaData(dep, datasource, packageName); expect(dep).toMatchObject({ sourceUrl: 'https://nope-nope-nope', }); }); it('Should handle non-url', () => { const dep = { sourceUrl: 'not-a-url', releases: [ { version: '5.7.0', releaseTimestamp: '2020-02-14T13:12:00.000Z' as Timestamp, }, { version: '5.6.1', releaseTimestamp: '2020-02-14T10:04:00.000Z' as Timestamp, }, ], }; const datasource = NpmDatasource.id; const packageName = 'dropzone'; addMetaData(dep, datasource, packageName); expect(dep).toEqual({ releases: [ { version: '5.7.0', releaseTimestamp: '2020-02-14T13:12:00.000Z', }, { version: '5.6.1', releaseTimestamp: '2020-02-14T10:04:00.000Z', }, ], }); }); it('Should handle parsing/converting of GitHub sourceUrls with http and www correctly', () => { const dep = { sourceUrl: 'http://www.github.com/mockk/mockk/', releases: [{ version: '1.9.3' }], }; const datasource = MavenDatasource.id; const packageName = 'io.mockk:mockk'; addMetaData(dep, datasource, packageName); expect(dep.sourceUrl).toBe('https://github.com/mockk/mockk'); }); it('Should move github homepage to sourceUrl', () => { const dep = { homepage: 'http://www.github.com/mockk/mockk/', releases: [{ version: '1.9.3' }], sourceUrl: undefined, }; const datasource = MavenDatasource.id; const packageName = 'io.mockk:mockk'; addMetaData(dep, datasource, packageName); expect(dep.sourceUrl).toBe('https://github.com/mockk/mockk'); expect(dep.homepage).toBeUndefined(); }); it('Should handle parsing/converting of GitLab sourceUrls with http and www correctly', () => { const dep = { sourceUrl: 'http://gitlab.com/meno/dropzone/', releases: [{ version: '5.7.0' }], }; const datasource = MavenDatasource.id; const packageName = 'dropzone'; addMetaData(dep, datasource, packageName); expect(dep.sourceUrl).toBe('https://gitlab.com/meno/dropzone'); }); it('Should normalize releaseTimestamp', () => { const dep = { releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56' as Timestamp, }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z' as Timestamp, }, { version: '1.0.3', releaseTimestamp: '2000-01-03T14:34:56.000+02:00' as Timestamp, }, { version: '1.0.4', releaseTimestamp: '20000103150210' as Timestamp }, ], }; addMetaData(dep, MavenDatasource.id, 'foobar'); expect(dep.releases).toMatchObject([ { releaseTimestamp: '2000-01-01T12:34:56.000Z' }, { releaseTimestamp: '2000-01-02T12:34:56.000Z' }, { releaseTimestamp: '2000-01-03T12:34:56.000Z' }, { releaseTimestamp: '2000-01-03T15:02:10.000Z' }, ]); }); describe('massageUrl', () => { it('Should return an empty string when massaging an invalid url', () => { expect(massageUrl('not a url')).toMatch(''); }); it.each` sourceUrl ${'git@github.com:user/repo'} ${'http://github.com/user/repo'} ${'http+git://github.com/user/repo'} ${'https+git://github.com/user/repo'} ${'ssh://git@github.com/user/repo'} ${'git://github.com/user/repo'} ${'https://www.github.com/user/repo'} ${'https://user.github.com/repo'} `('Should massage GitHub url $sourceUrl', ({ sourceUrl }) => { expect(massageUrl(sourceUrl)).toBe('https://github.com/user/repo'); }); it.each` sourceUrl ${'http://gitlab.com/user/repo'} ${'git://gitlab.com/user/repo'} ${'https://gitlab.com/user/repo/tree/master'} ${'http://gitlab.com/user/repo/'} ${'http://gitlab.com/user/repo.git'} ${'git@gitlab.com:user/repo.git'} `('Should massage GitLab url $sourceUrl', ({ sourceUrl }) => { expect(massageUrl(sourceUrl)).toBe('https://gitlab.com/user/repo'); }); it.each` sourceUrl ${'git@example.com:user/repo'} ${'http://example.com/user/repo'} ${'http+git://example.com/user/repo'} ${'https+git://example.com/user/repo'} ${'ssh://git@example.com/user/repo'} ${'git://example.com/user/repo'} `('Should massage other sourceUrl $sourceUrl', ({ sourceUrl }) => { expect(massageUrl(sourceUrl)).toBe('https://example.com/user/repo'); }); }); it('Should massage github git@ url to valid https url', () => { expect(massageGithubUrl('git@example.com:foo/bar')).toMatch( 'https://example.com/foo/bar', ); }); it('Should massage github http url to valid https url', () => { expect(massageGithubUrl('http://example.com/foo/bar')).toMatch( 'https://example.com/foo/bar', ); }); it('Should massage github http and git url to valid https url', () => { expect(massageGithubUrl('http+git://example.com/foo/bar')).toMatch( 'https://example.com/foo/bar', ); }); it('Should massage github ssh git@ url to valid https url', () => { expect(massageGithubUrl('ssh://git@example.com/foo/bar')).toMatch( 'https://example.com/foo/bar', ); }); it('Should massage github git url to valid https url', () => { expect(massageGithubUrl('git://example.com/foo/bar')).toMatch( 'https://example.com/foo/bar', ); }); it('Should massage gitlab git url to valid https url', () => { expect( massageGitlabUrl('git://example.gitlab-dedicated.com/foo/bar'), ).toMatch('https://example.gitlab-dedicated.com/foo/bar'); }); it('Should remove homepage when homepage and sourceUrl are same', () => { const dep = { homepage: 'https://github.com/foo/bar', sourceUrl: 'https://github.com/foo/bar', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56' as Timestamp, }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z' as Timestamp, }, { version: '1.0.3', releaseTimestamp: '2000-01-03T14:34:56.000+02:00' as Timestamp, }, ], }; addMetaData(dep, MavenDatasource.id, 'foobar'); expect(dep).toMatchObject({ releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56.000Z', }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z', }, { version: '1.0.3', releaseTimestamp: '2000-01-03T12:34:56.000Z', }, ], sourceUrl: 'https://github.com/foo/bar', }); }); it('Should delete gitlab homepage if its same as sourceUrl', () => { const dep = { sourceUrl: 'https://gitlab.com/meno/repo', homepage: 'https://gitlab.com/meno/repo', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56' as Timestamp, }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z' as Timestamp, }, { version: '1.0.3', releaseTimestamp: '2000-01-03T14:34:56.000+02:00' as Timestamp, }, ], }; addMetaData(dep, MavenDatasource.id, 'foobar'); expect(dep).toMatchObject({ sourceUrl: 'https://gitlab.com/meno/repo', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56.000Z', }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z', }, { version: '1.0.3', releaseTimestamp: '2000-01-03T12:34:56.000Z', }, ], }); }); it('does not set homepage to sourceURl when undefined', () => { const dep = { sourceUrl: 'https://gitlab.com/meno/repo', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56' as Timestamp, }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z' as Timestamp, }, { version: '1.0.3', releaseTimestamp: '2000-01-03T14:34:56.000+02:00' as Timestamp, }, ], }; addMetaData(dep, MavenDatasource.id, 'foobar'); expect(dep).toMatchObject({ sourceUrl: 'https://gitlab.com/meno/repo', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56.000Z', }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z', }, { version: '1.0.3', releaseTimestamp: '2000-01-03T12:34:56.000Z', }, ], }); }); it('does not set homepage to sourceURl when not github or gitlab', () => { const dep = { homepage: 'https://somesource.com/', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56' as Timestamp, }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z' as Timestamp, }, { version: '1.0.3', releaseTimestamp: '2000-01-03T14:34:56.000+02:00' as Timestamp, }, ], }; addMetaData(dep, MavenDatasource.id, 'foobar'); expect(dep).toMatchObject({ homepage: 'https://somesource.com/', releases: [ { version: '1.0.1', releaseTimestamp: '2000-01-01T12:34:56.000Z', }, { version: '1.0.2', releaseTimestamp: '2000-01-02T12:34:56.000Z', }, { version: '1.0.3', releaseTimestamp: '2000-01-03T12:34:56.000Z', }, ], }); }); it.each` sourceUrl | homepage | expected ${'not a url'} | ${'https://gitlab.com/org/repo'} | ${false} ${'https://gitlab.com/org/repo'} | ${'not a url'} | ${false} ${'https://gitlab.com/org'} | ${'https://gitlab.com/org/'} | ${true} ${'https://gitlab.com/org/repo/'} | ${'https://gitlab.com/org/repo'} | ${true} ${'https://github.com/org/repo/path/'} | ${'https://github.com/org/repo/path/'} | ${false} ${'https://gitlab.com/org/repo/'} | ${'https://gitlab.com/org/repo/path/to/something/'} | ${false} ${'https://gitlab.com/org/repo/'} | ${null} | ${false} ${'https://gitlab.com/org/repo/'} | ${undefined} | ${false} ${'https://gitlab.com/org/repo/'} | ${'github.com'} | ${false} ${'https://github.com/bitnami/charts'} | ${'https://github.com/bitnami/charts/tree/master/bitnami/kube-prometheus'} | ${false} `( 'shouldDeleteHomepage($sourceUrl, $homepage) -> $expected', ({ sourceUrl, homepage, expected }) => { expect(shouldDeleteHomepage(sourceUrl, homepage)).toBe(expected); }, ); // for coverage it('should handle dep with no releases', () => { const dep = partial({}); const datasource = PypiDatasource.id; const packageName = 'pycountry'; addMetaData(dep, datasource, packageName); expect(dep).toEqual({ changelogUrl: 'https://github.com/flyingcircusio/pycountry/blob/master/HISTORY.txt', sourceUrl: 'https://github.com/flyingcircusio/pycountry', }); }); });