Skip to content
Snippets Groups Projects
Unverified Commit a3e2b2ff authored by Caleb Cartwright's avatar Caleb Cartwright Committed by GitHub
Browse files

handle null licenses in crates.io response schema, run [crates] (#7074)


* fix: handle null licenses in crates.io response schema

* more tests for crates license badge

Co-authored-by: default avatarrepo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
parent 28cf98ff
No related branches found
No related tags found
No related merge requests found
...@@ -14,7 +14,7 @@ const crateSchema = Joi.object({ ...@@ -14,7 +14,7 @@ const crateSchema = Joi.object({
.items( .items(
Joi.object({ Joi.object({
downloads: nonNegativeInteger, downloads: nonNegativeInteger,
license: Joi.string().required(), license: Joi.string().required().allow(null),
}) })
) )
.min(1) .min(1)
...@@ -25,7 +25,7 @@ const versionSchema = Joi.object({ ...@@ -25,7 +25,7 @@ const versionSchema = Joi.object({
version: Joi.object({ version: Joi.object({
downloads: nonNegativeInteger, downloads: nonNegativeInteger,
num: Joi.string().required(), num: Joi.string().required(),
license: Joi.string().required(), license: Joi.string().required().allow(null),
}).required(), }).required(),
}).required() }).required()
......
import { ServiceTester } from '../tester.js'
import { isMetric } from '../test-validators.js' import { isMetric } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = new ServiceTester({ export const t = await createServiceTester()
id: 'crates',
title: 'crates.io',
pathPrefix: '/crates',
})
t.create('total downloads') t.create('total downloads')
.get('/d/libc.json') .get('/d/libc.json')
......
import { InvalidResponse } from '../index.js'
import { BaseCratesService, keywords } from './crates-base.js' import { BaseCratesService, keywords } from './crates-base.js'
export default class CratesLicense extends BaseCratesService { export default class CratesLicense extends BaseCratesService {
...@@ -21,28 +22,30 @@ export default class CratesLicense extends BaseCratesService { ...@@ -21,28 +22,30 @@ export default class CratesLicense extends BaseCratesService {
}, },
] ]
static render({ license }) { static defaultBadgeData = { label: 'license', color: 'blue' }
return {
label: 'license', static render({ license: message }) {
message: license, return { message }
color: 'blue',
} }
static transform({ errors, version, versions }) {
// crates.io returns a 200 response with an errors object in
// error scenarios, e.g. https://crates.io/api/v1/crates/libc/0.1
if (errors) {
throw new InvalidResponse({ prettyMessage: errors[0].detail })
} }
async handle({ crate, version }) { const license = version ? version.license : versions[0].license
const json = await this.fetch({ crate, version }) if (!license) {
throw new InvalidResponse({ prettyMessage: 'invalid null license' })
}
if (json.errors) { return { license }
/* a call like
https://crates.io/api/v1/crates/libc/0.1
or
https://crates.io/api/v1/crates/libc/0.1.76
returns a 200 OK with an errors object */
return { message: json.errors[0].detail }
} }
return this.constructor.render({ async handle({ crate, version }) {
license: json.version ? json.version.license : json.versions[0].license, const json = await this.fetch({ crate, version })
}) const { license } = this.constructor.transform(json)
return this.constructor.render({ license })
} }
} }
import { expect } from 'chai'
import { test, given } from 'sazerac'
import { InvalidResponse } from '../index.js'
import CratesLicense from './crates-license.service.js'
describe('CratesLicense', function () {
test(CratesLicense.transform, () => {
given({
version: { num: '1.0.0', license: 'MIT' },
versions: [{ license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT' })
given({
versions: [{ license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT/Apache 2.0' })
})
it('throws InvalidResponse on error response', function () {
expect(() =>
CratesLicense.transform({ errors: [{ detail: 'invalid semver' }] })
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid semver')
})
it('throws InvalidResponse on null license with specific version', function () {
expect(() =>
CratesLicense.transform({ version: { num: '1.2.3', license: null } })
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid null license')
})
it('throws InvalidResponse on null license with latest version', function () {
expect(() => CratesLicense.transform({ versions: [{ license: null }] }))
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid null license')
})
})
import { ServiceTester } from '../tester.js' import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
export const t = new ServiceTester({
id: 'crates',
title: 'crates.io',
pathPrefix: '/crates/l',
})
t.create('license') t.create('license')
.get('/libc.json') .get('/libc.json')
...@@ -16,4 +11,13 @@ t.create('license (with version)') ...@@ -16,4 +11,13 @@ t.create('license (with version)')
t.create('license (not found)') t.create('license (not found)')
.get('/not-a-real-package.json') .get('/not-a-real-package.json')
.expectBadge({ label: 'crates.io', message: 'not found' }) .expectBadge({ label: 'license', message: 'not found' })
// https://github.com/badges/shields/issues/7073
t.create('license (null licenses in history)')
.get('/stun.json')
.expectBadge({ label: 'license', message: 'MIT/Apache-2.0' })
t.create('license (version with null license)')
.get('/stun/0.0.1.json')
.expectBadge({ label: 'license', message: 'invalid null license' })
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment