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',
});
});
});
});