import { isString } from '@sindresorhus/is'; import { codeBlock } from 'common-tags'; import { Fixtures } from '~test/fixtures.ts'; import * as httpMock from '~test/http-mock.ts'; import * as _packageCache from '../../../util/cache/package/index.ts'; import { regEx } from '../../../util/regex.ts'; import * as urlUtil from '../../../util/url.ts'; import * as mavenVersioning from '../../versioning/maven/index.ts'; import { getPkgReleases } from '../index.ts'; import { MAVEN_REPO } from '../maven/common.ts'; import * as mavenUtil from '../maven/util.ts'; import { SbtPackageDatasource } from './index.ts'; import { extractPageLinks } from './util.ts'; vi.mock('../../../util/cache/package/index.ts'); const packageCache = vi.mocked(_packageCache); describe('modules/datasource/sbt-package/index', () => { it('parses Maven index directory', () => { expect( extractPageLinks(Fixtures.get(`maven-index.html`), (x) => regEx(/^\.+/).test(x) ? null : x, ), ).toEqual([ 'autofix-3.0.6_2.11', 'sbt-scalatest_2.12_1.0', 'scalatest', 'scalatest-app_native0.4_3', 'scalatest_2.12', 'scalatest_2.13', 'scalatest_2.13.0-RC1', 'scalatest_3', 'scalatest_sjs1.0.0-M7_2.13.0-RC2', 'test-interface', ]); }); it('parses sbt index directory', () => { expect( extractPageLinks(Fixtures.get(`sbt-plugins-index.html`), (x) => regEx(/^\.+/).test(x) ? null : x, ), ).toEqual([ 'au.com.onegeek', 'ch', 'com.github.DavidPerezIngeniero', 'org.portable-scala', 'scalajs-react-interface', 'uk.co.josephearl', 'woshilaiceshide', ]); }); it('uses proper hostType', () => { const ds = new SbtPackageDatasource(); expect(ds).toMatchObject({ id: SbtPackageDatasource.id, http: { hostType: 'sbt' }, }); }); describe('getPkgReleases', () => { it('returns null in case of errors', async () => { httpMock .scope('https://failed_repo') .get('/maven/org/scalatest/') .reply(404) .get('/maven/org.scalatest/') .reply(404) .get('/maven/org/scalatest/scalatest/maven-metadata.xml') .reply(404); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.scalatest:scalatest', registryUrls: ['https://failed_repo/maven'], }); expect(res).toBeNull(); }); it('returns null if there is no version', async () => { httpMock .scope('https://repo.maven.apache.org') .get('/maven2/com/example/') .reply( 200, codeBlock` empty_2.12/ ??? `, ) .get('/maven2/com/example/empty_but_invalid/') .reply(404, '') .get('/maven2/com/example/empty/maven-metadata.xml') .reply(404) .get('/maven2/com/example/empty/') .reply(404); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'com.example:empty', registryUrls: [], }); expect(res).toBeNull(); }); it('fetches releases from Maven', async () => { httpMock .scope('https://repo.maven.apache.org/maven2/') .get('/org/example/') .reply( 200, codeBlock` ../ example_2.12/ example_2.12/ example_native/ example_sjs/ `, ) .get('/org/example/example/') .reply( 200, codeBlock` ../ 1.2.0/ `, ) .get('/org/example/example_2.12/') .reply( 200, codeBlock` ../ 1.2.3/ `, ) .get('/org/example/example/1.2.3/example-1.2.3.pom') .reply(404) .get('/org/example/example_2.12/1.2.3/example-1.2.3.pom') .reply(404) .get('/org/example/example_2.12/1.2.3/example_2.12-1.2.3.pom') .reply(404); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example', registryUrls: [MAVEN_REPO], }); expect(res).toEqual({ dependencyUrl: 'https://repo.maven.apache.org/maven2/org/example', registryUrl: 'https://repo.maven.apache.org/maven2', releases: [{ version: '1.2.0' }, { version: '1.2.3' }], }); }); it('fetches Maven releases with Scala version', async () => { httpMock .scope('https://repo.maven.apache.org/maven2/') .get('/org/example/') .reply( 200, `example_2.12/`, ) .get('/org/example/example_2.12/') .reply(200, `1.2.3/`) .get('/org/example/example_2.12/1.2.3/example-1.2.3.pom') .reply(404) .get('/org/example/example_2.12/1.2.3/example_2.12-1.2.3.pom') .reply(404); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example_2.12', registryUrls: [], }); expect(res).toEqual({ dependencyUrl: 'https://repo.maven.apache.org/maven2/org/example', registryUrl: 'https://repo.maven.apache.org/maven2', releases: [{ version: '1.2.3' }], }); }); it('fetches releases from Confluent', async () => { httpMock .scope('https://packages.confluent.io/maven/io/confluent') .get('/') .reply( 200, 'kafka-avro-serializer/', ) .get('/kafka-avro-serializer/') .reply( 200, '7.0.1/', ) .get('/kafka-avro-serializer/7.0.1/kafka-avro-serializer-7.0.1.pom') .reply( 200, codeBlock` z kafka-avro-serializer jar kafka-avro-serializer `, ); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'io.confluent:kafka-avro-serializer', registryUrls: ['https://packages.confluent.io/maven'], }); expect(res).toEqual({ dependencyUrl: 'https://packages.confluent.io/maven/io/confluent', registryUrl: 'https://packages.confluent.io/maven', releases: [{ version: '7.0.1' }], }); }); it('extracts URL from Maven POM file', async () => { httpMock .scope('https://repo.maven.apache.org/maven2/') .get('/org/example/') .reply(200, `example_2.12/`) .get('/org/example/example/') .reply(200, `1.2.3/`) .get('/org/example/example/1.2.3/example-1.2.3.pom') .reply( 200, codeBlock` https://package.example.org/about https://example.org/repo.git `, ); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example', registryUrls: [MAVEN_REPO], }); expect(res).toMatchObject({ homepage: 'https://package.example.org/about', sourceUrl: 'https://example.org/repo', releases: [{ version: '1.2.3' }], }); }); it('falls back to Maven for orgarization root folder non-listable repositories', async () => { httpMock .scope('https://gitlab.com/api/v4/projects/123/packages/maven/') .get('/org/example/') .reply(404) .get('/org.example/') .reply(404) .get('/org/example/example_2.13/maven-metadata.xml') .reply( 200, codeBlock` org.example package 1.2.3 1.2.3 1.2.3 `, ) .get('/org/example/example_2.13/1.2.3/example_2.13-1.2.3.pom') .reply(404); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example_2.13', registryUrls: [ 'https://gitlab.com/api/v4/projects/123/packages/maven/', ], }); expect(res).toMatchObject({}); }); it('continues when parseUrl returns null for packageRootUrl', async () => { httpMock .scope('https://repo.maven.apache.org/maven2/') .get('/org/example/example/maven-metadata.xml') .reply(404); const downloadSpy = vi .spyOn(mavenUtil, 'downloadHttpContent') .mockResolvedValueOnce( `example/`, ) .mockResolvedValueOnce(null); // dot-separated group URL returns nothing const realParseUrl = urlUtil.parseUrl; const parseUrlSpy = vi .spyOn(urlUtil, 'parseUrl') .mockImplementation((url) => { if ( isString(url) && url === 'https://repo.maven.apache.org/maven2/org/example/' ) { return null; } return realParseUrl(url); }); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example', registryUrls: [MAVEN_REPO], }); parseUrlSpy.mockRestore(); downloadSpy.mockRestore(); expect(res).toBeNull(); }); it('skips pkgUrl when parseUrl returns null for it', async () => { httpMock .scope('https://repo.maven.apache.org/maven2/') .get('/org/example/example/maven-metadata.xml') .reply(404); const downloadSpy = vi .spyOn(mavenUtil, 'downloadHttpContent') .mockResolvedValueOnce( `example/`, ) .mockResolvedValueOnce(null) // dot-separated group URL returns nothing .mockResolvedValueOnce(`1.2.3/`); // artifact subdir const realParseUrl = urlUtil.parseUrl; const parseUrlSpy = vi .spyOn(urlUtil, 'parseUrl') .mockImplementation((url) => { if ( isString(url) && url === 'https://repo.maven.apache.org/maven2/org/example/example/' ) { return null; } return realParseUrl(url); }); const res = await getPkgReleases({ versioning: mavenVersioning.id, datasource: SbtPackageDatasource.id, packageName: 'org.example:example', registryUrls: [MAVEN_REPO], }); parseUrlSpy.mockRestore(); downloadSpy.mockRestore(); expect(res).toBeNull(); }); }); describe('postprocessRelease', () => { const datasource = new SbtPackageDatasource(); it('extracts URL from Maven POM file', async () => { const registryUrl = 'https://repo.maven.apache.org/maven2/'; const packageName = 'org.example:example'; packageCache.get.mockImplementation((ns: string, k: string) => ns === 'datasource-sbt-package' && k === `package-urls:${registryUrl}:${packageName}` ? Promise.resolve([`${registryUrl}org/example/`]) : Promise.resolve(undefined), ); httpMock .scope(registryUrl) .get('/org/example/1.2.3/example-1.2.3.pom') .reply(200, codeBlock``, { 'last-modified': 'Wed, 21 Oct 2015 07:28:00 GMT', }); const res = await datasource.postprocessRelease( { packageName, registryUrl }, { version: '1.2.3' }, ); expect(res).toEqual({ version: '1.2.3', releaseTimestamp: '2015-10-21T07:28:00.000Z', }); }); }); });