From c3f4bd8be421d345c33c39365a94d5d008574d48 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 14:10:16 -0400 Subject: [PATCH 01/13] Syntactic sugar for tester registration --- package-lock.json | 36 +++++++++++-------- package.json | 1 + .../librariesio-dependent-repos.tester.js | 6 +--- .../librariesio-dependents.tester.js | 6 +--- .../librariesio-sourcerank.tester.js | 6 +--- services/service-tester.js | 28 +++++++++++++++ 6 files changed, 54 insertions(+), 29 deletions(-) diff --git a/package-lock.json b/package-lock.json index 57e99aa46dd40..efcc3a810be52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2211,26 +2211,17 @@ } } }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } + "caller": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/caller/-/caller-1.0.1.tgz", + "integrity": "sha1-uFGGD3Dhlds9J3OVqhp+I+ow7PU=", + "dev": true }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -12395,6 +12386,23 @@ "requires": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" + }, + "dependencies": { + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + } } }, "resolve": { diff --git a/package.json b/package.json index 67950e268cac4..f2ab85d306db8 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "babel-eslint": "^8.0.2", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", + "caller": "^1.0.1", "chai": "^4.1.2", "chai-as-promised": "^7.1.1", "chai-string": "^1.4.0", diff --git a/services/librariesio/librariesio-dependent-repos.tester.js b/services/librariesio/librariesio-dependent-repos.tester.js index e159090c6aa49..2624beb4bb045 100644 --- a/services/librariesio/librariesio-dependent-repos.tester.js +++ b/services/librariesio/librariesio-dependent-repos.tester.js @@ -4,11 +4,7 @@ const Joi = require('joi') const ServiceTester = require('../service-tester') const { isMetric } = require('../test-validators') -const t = new ServiceTester({ - id: 'librariesio-dependent-repos', - title: 'Libraries.io dependent repos', - pathPrefix: '/librariesio/dependent-repos', -}) +const t = ServiceTester.forThisService() module.exports = t t.create('dependent repo count') diff --git a/services/librariesio/librariesio-dependents.tester.js b/services/librariesio/librariesio-dependents.tester.js index 8a0bf1dd5437d..fb7ecacb35d6f 100644 --- a/services/librariesio/librariesio-dependents.tester.js +++ b/services/librariesio/librariesio-dependents.tester.js @@ -4,11 +4,7 @@ const Joi = require('joi') const ServiceTester = require('../service-tester') const { isMetric } = require('../test-validators') -const t = new ServiceTester({ - id: 'librariesio-dependents', - title: 'Libraries.io dependents', - pathPrefix: '/librariesio/dependents', -}) +const t = ServiceTester.forThisService() module.exports = t t.create('dependent count') diff --git a/services/librariesio/librariesio-sourcerank.tester.js b/services/librariesio/librariesio-sourcerank.tester.js index 5cfd2ef4b6185..b3c1600d44a39 100644 --- a/services/librariesio/librariesio-sourcerank.tester.js +++ b/services/librariesio/librariesio-sourcerank.tester.js @@ -4,11 +4,7 @@ const Joi = require('joi') const ServiceTester = require('../service-tester') const { anyInteger } = require('../validators') -const t = new ServiceTester({ - id: 'librariesio-sourcerank', - title: 'Libraries.io SourceRank', - pathPrefix: '/librariesio/sourcerank', -}) +const t = ServiceTester.forThisService() module.exports = t t.create('sourcerank') diff --git a/services/service-tester.js b/services/service-tester.js index db591535a1cab..090bf6fb0037c 100644 --- a/services/service-tester.js +++ b/services/service-tester.js @@ -1,7 +1,9 @@ 'use strict' const frisby = require('icedfrisby-nock')(require('icedfrisby')) +const caller = require('caller') const config = require('../lib/test-config') +const BaseService = require('./base') /** * Encapsulate a suite of tests. Create new tests using create() and register @@ -27,6 +29,32 @@ class ServiceTester { }) } + static forServiceClass(ServiceClass) { + const id = ServiceClass.name + const pathPrefix = `/${ServiceClass.url.base}` + return new this({ + id, + title: id, + pathPrefix, + }) + } + + static forThisService() { + const servicePath = caller().replace('.tester.js', '.service.js') + let ServiceClass + try { + ServiceClass = require(servicePath) + } catch (e) { + throw Error(`Couldn't load service from ${servicePath}`) + } + if (!(ServiceClass.prototype instanceof BaseService)) { + throw Error( + `${servicePath} does not export a single service. Invoke new ServiceTester() directly.` + ) + } + return this.forServiceClass(ServiceClass) + } + /** * Invoked before each test. This is a stub which can be overridden on * instances. From e1c67578150eeff30144f420fa77d109d28ce039 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 14:44:20 -0400 Subject: [PATCH 02/13] Split up NPM tester --- services/npm/npm-downloads.tester.js | 72 ++++++ services/npm/npm-license.tester.js | 134 +++++++++++ services/npm/npm-type-definitions.tester.js | 21 ++ services/npm/npm-version.tester.js | 55 +++++ services/npm/npm.tester.js | 239 -------------------- 5 files changed, 282 insertions(+), 239 deletions(-) create mode 100644 services/npm/npm-downloads.tester.js create mode 100644 services/npm/npm-license.tester.js create mode 100644 services/npm/npm-type-definitions.tester.js create mode 100644 services/npm/npm-version.tester.js delete mode 100644 services/npm/npm.tester.js diff --git a/services/npm/npm-downloads.tester.js b/services/npm/npm-downloads.tester.js new file mode 100644 index 0000000000000..c1dea96c40026 --- /dev/null +++ b/services/npm/npm-downloads.tester.js @@ -0,0 +1,72 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') +const { isMetric } = require('../test-validators') +const colorscheme = require('../../lib/colorscheme.json') +const mapValues = require('lodash.mapvalues') + +const t = new ServiceTester({ + id: 'NpmDownloads', + title: 'NpmDownloads', + pathPrefix: '/npm', +}) +module.exports = t + +const colorsB = mapValues(colorscheme, 'colorB') + +t.create('total downloads of left-pad') + .get('/dt/left-pad.json?style=_shields_test') + .expectJSONTypes( + Joi.object().keys({ + name: 'downloads', + value: isMetric, + colorB: colorsB.brightgreen, + }) + ) + +t.create('total downloads of @cycle/core') + .get('/dt/@cycle/core.json') + .expectJSONTypes(Joi.object().keys({ name: 'downloads', value: isMetric })) + +t.create('total downloads of package with zero downloads') + .get('/dt/package-no-downloads.json?style=_shields_test') + .intercept(nock => + nock('https://api.npmjs.org') + .get('/downloads/range/1000-01-01:3000-01-01/package-no-downloads') + .reply(200, { + downloads: [{ downloads: 0, day: '2018-01-01' }], + }) + ) + .expectJSON({ name: 'downloads', value: '0', colorB: colorsB.red }) + +t.create('exact total downloads value') + .get('/dt/exact-value.json') + .intercept(nock => + nock('https://api.npmjs.org') + .get('/downloads/range/1000-01-01:3000-01-01/exact-value') + .reply(200, { + downloads: [ + { downloads: 2, day: '2018-01-01' }, + { downloads: 3, day: '2018-01-02' }, + ], + }) + ) + .expectJSON({ name: 'downloads', value: '5' }) + +t.create('total downloads when network is off') + .get('/dt/@cycle/core.json?style=_shields_test') + .networkOff() + .expectJSON({ + name: 'downloads', + value: 'inaccessible', + colorB: colorsB.lightgray, + }) + +t.create('total downloads of unknown package') + .get('/dt/npm-api-does-not-have-this-package.json?style=_shields_test') + .expectJSON({ + name: 'downloads', + value: 'package not found or too new', + colorB: colorsB.red, + }) diff --git a/services/npm/npm-license.tester.js b/services/npm/npm-license.tester.js new file mode 100644 index 0000000000000..df048b8ecd712 --- /dev/null +++ b/services/npm/npm-license.tester.js @@ -0,0 +1,134 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') +const colorscheme = require('../../lib/colorscheme.json') +const mapValues = require('lodash.mapvalues') + +const t = ServiceTester.forThisService() +module.exports = t +const colorsB = mapValues(colorscheme, 'colorB') + +t + .create('gets the license of express') + .get('/express.json') + .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) + +t + .create('gets the license of express from a custom registry') + .get('/express.json?registry_uri=https://registry.npmjs.com') + .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) + +t + .create('public domain license') + .get('/redux-auth.json?style=_shields_test') + .expectJSON({ name: 'license', value: 'WTFPL', colorB: '#7cd958' }) + +t + .create('copyleft license') + .get('/trianglify.json?style=_shields_test') + .expectJSON({ name: 'license', value: 'GPL-3.0', colorB: colorsB.orange }) + +t + .create('permissive license') + .get('/express.json?style=_shields_test') + .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) + +t + .create('permissive license for scoped package') + .get('/@cycle%2Fcore.json?style=_shields_test') + .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) + +t + .create( + 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)' + ) + .get('/rho-cc-promise.json?style=_shields_test') + .expectJSON({ + name: 'license', + value: '(MPL-2.0 OR MIT)', + colorB: colorsB.lightgrey, + }) + +t + .create('license for package without a license property') + .get('/package-without-license.json?style=_shields_test') + .intercept(nock => + nock('https://registry.npmjs.org') + .get('/package-without-license/latest') + .reply(200, { + name: 'package-without-license', + }) + ) + .expectJSON({ name: 'license', value: 'missing', colorB: colorsB.red }) + +t + .create('license for package with a license object') + .get('/package-license-object.json?style=_shields_test') + .intercept(nock => + nock('https://registry.npmjs.org') + .get('/package-license-object/latest') + .reply(200, { + name: 'package-license-object', + license: { + type: 'MIT', + url: 'https://www.opensource.org/licenses/mit-license.php', + }, + }) + ) + .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) + +t + .create('license for package with a license array') + .get('/package-license-array.json?style=_shields_test') + .intercept(nock => + nock('https://registry.npmjs.org') + .get('/package-license-array/latest') + .reply(200, { + name: 'package-license-object', + license: ['MPL-2.0', 'MIT'], + }) + ) + .expectJSON({ + name: 'license', + value: 'MPL-2.0, MIT', + colorB: colorsB.lightgrey, + }) + +t + .create('license for unknown package') + .get('/npm-registry-does-not-have-this-package.json?style=_shields_test') + .expectJSON({ + name: 'license', + value: 'package not found', + colorB: colorsB.red, + }) + +t + .create('license when network is off') + .get('/pakage-network-off.json?style=_shields_test') + .networkOff() + .expectJSON({ + name: 'license', + value: 'inaccessible', + colorB: colorsB.lightgrey, + }) + +// This tests error-handling functionality in NpmBase. +t + .create('when json is malformed for scoped package') + .get('/@cycle%2Fcore.json') + .intercept(nock => + nock('https://registry.npmjs.org') + .get('/@cycle%2Fcore') + .reply(200, { + 'dist-tags': { + latest: '1.2.3', + }, + versions: null, + }) + ) + .expectJSON({ + name: 'license', + value: 'invalid json response', + }) diff --git a/services/npm/npm-type-definitions.tester.js b/services/npm/npm-type-definitions.tester.js new file mode 100644 index 0000000000000..338ac227915b0 --- /dev/null +++ b/services/npm/npm-type-definitions.tester.js @@ -0,0 +1,21 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const t = ServiceTester.forThisService() +module.exports = t + +const isTypeDefinition = Joi.string().regex( + /^(Flow|TypeScript) v?[0-9]+.[0-9]+( \| (Flow|TypeScript) v?[0-9]+.[0-9]+)?$/ +) + +t.create('types') + .get('/chalk.json') + .expectJSONTypes( + Joi.object().keys({ name: 'type definitions', value: isTypeDefinition }) + ) + +t.create('no types') + .get('/left-pad.json') + .expectJSON({ name: 'type definitions', value: 'none' }) diff --git a/services/npm/npm-version.tester.js b/services/npm/npm-version.tester.js new file mode 100644 index 0000000000000..6f635097d43d3 --- /dev/null +++ b/services/npm/npm-version.tester.js @@ -0,0 +1,55 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') +const { isSemver } = require('../test-validators') + +const t = ServiceTester.forThisService() +module.exports = t + +t.create('gets the package version of left-pad') + .get('/left-pad.json') + .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) + +t.create('gets the package version of left-pad from a custom registry') + .get('/left-pad.json?registry_uri=https://registry.npmjs.com') + .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) + +t.create('gets the package version of @cycle/core') + .get('/@cycle/core.json') + .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) + +t.create('gets a tagged package version of npm') + .get('/npm/next.json') + .expectJSONTypes(Joi.object().keys({ name: 'npm@next', value: isSemver })) + +t.create('gets the correct tagged package version of npm') + .intercept(nock => + nock('https://registry.npmjs.org') + .get('/-/package/npm/dist-tags') + .reply(200, { latest: '1.2.3', next: '4.5.6' }) + ) + .get('/npm/next.json') + .expectJSON({ name: 'npm@next', value: 'v4.5.6' }) + +t.create('returns an error for version with an invalid tag') + .get('/npm/frodo.json') + .expectJSON({ name: 'npm', value: 'tag not found' }) + +t.create('gets the package version of left-pad from a custom registry') + .get('/left-pad.json?registry_uri=https://registry.npmjs.com') + .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) + +t.create('gets the tagged package version of @cycle/core') + .get('/@cycle/core/canary.json') + .expectJSONTypes(Joi.object().keys({ name: 'npm@canary', value: isSemver })) + +t.create( + 'gets the tagged package version of @cycle/core from a custom registry' +) + .get('/@cycle/core/canary.json?registry_uri=https://registry.npmjs.com') + .expectJSONTypes(Joi.object().keys({ name: 'npm@canary', value: isSemver })) + +t.create('invalid package name') + .get('/frodo-is-not-a-package.json') + .expectJSON({ name: 'npm', value: 'package not found' }) diff --git a/services/npm/npm.tester.js b/services/npm/npm.tester.js deleted file mode 100644 index 3f1cd59f78387..0000000000000 --- a/services/npm/npm.tester.js +++ /dev/null @@ -1,239 +0,0 @@ -'use strict' - -const Joi = require('joi') -const ServiceTester = require('../service-tester') -const { isMetric, isSemver } = require('../test-validators') -const colorscheme = require('../../lib/colorscheme.json') -const mapValues = require('lodash.mapvalues') - -const t = new ServiceTester({ id: 'npm', title: 'NPM' }) -module.exports = t -const colorsB = mapValues(colorscheme, 'colorB') - -const isTypeDefinition = Joi.string().regex( - /^(Flow|TypeScript) v?[0-9]+.[0-9]+( \| (Flow|TypeScript) v?[0-9]+.[0-9]+)?$/ -) - -t.create('total downloads of left-pad') - .get('/dt/left-pad.json?style=_shields_test') - .expectJSONTypes( - Joi.object().keys({ - name: 'downloads', - value: isMetric, - colorB: colorsB.brightgreen, - }) - ) - -t.create('total downloads of @cycle/core') - .get('/dt/@cycle/core.json') - .expectJSONTypes(Joi.object().keys({ name: 'downloads', value: isMetric })) - -t.create('total downloads of package with zero downloads') - .get('/dt/package-no-downloads.json?style=_shields_test') - .intercept(nock => - nock('https://api.npmjs.org') - .get('/downloads/range/1000-01-01:3000-01-01/package-no-downloads') - .reply(200, { - downloads: [{ downloads: 0, day: '2018-01-01' }], - }) - ) - .expectJSON({ name: 'downloads', value: '0', colorB: colorsB.red }) - -t.create('exact total downloads value') - .get('/dt/exact-value.json') - .intercept(nock => - nock('https://api.npmjs.org') - .get('/downloads/range/1000-01-01:3000-01-01/exact-value') - .reply(200, { - downloads: [ - { downloads: 2, day: '2018-01-01' }, - { downloads: 3, day: '2018-01-02' }, - ], - }) - ) - .expectJSON({ name: 'downloads', value: '5' }) - -t.create('total downloads when network is off') - .get('/dt/@cycle/core.json?style=_shields_test') - .networkOff() - .expectJSON({ - name: 'downloads', - value: 'inaccessible', - colorB: colorsB.lightgray, - }) - -t.create('total downloads of unknown package') - .get('/dt/npm-api-does-not-have-this-package.json?style=_shields_test') - .expectJSON({ - name: 'downloads', - value: 'package not found or too new', - colorB: colorsB.red, - }) - -t.create('gets the package version of left-pad') - .get('/v/left-pad.json') - .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) - -t.create('gets the package version of @cycle/core') - .get('/v/@cycle/core.json') - .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) - -t.create('gets a tagged package version of npm') - .get('/v/npm/next.json') - .expectJSONTypes(Joi.object().keys({ name: 'npm@next', value: isSemver })) - -t.create('gets the correct tagged package version of npm') - .intercept(nock => - nock('https://registry.npmjs.org') - .get('/-/package/npm/dist-tags') - .reply(200, { latest: '1.2.3', next: '4.5.6' }) - ) - .get('/v/npm/next.json') - .expectJSON({ name: 'npm@next', value: 'v4.5.6' }) - -t.create('returns an error for version with an invalid tag') - .get('/v/npm/frodo.json') - .expectJSON({ name: 'npm', value: 'tag not found' }) - -t.create('gets the package version of left-pad from a custom registry') - .get('/v/left-pad.json?registry_uri=https://registry.npmjs.com') - .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) - -t.create('gets the tagged package version of @cycle/core') - .get('/v/@cycle/core/canary.json') - .expectJSONTypes(Joi.object().keys({ name: 'npm@canary', value: isSemver })) - -t.create( - 'gets the tagged package version of @cycle/core from a custom registry' -) - .get('/v/@cycle/core/canary.json?registry_uri=https://registry.npmjs.com') - .expectJSONTypes(Joi.object().keys({ name: 'npm@canary', value: isSemver })) - -t.create('gets the license of express') - .get('/l/express.json') - .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) - -t.create('gets the license of express from a custom registry') - .get('/l/express.json?registry_uri=https://registry.npmjs.com') - .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) - -t.create('invalid package name') - .get('/v/frodo-is-not-a-package.json') - .expectJSON({ name: 'npm', value: 'package not found' }) - -t.create('gets the package version of left-pad from a custom registry') - .get('/v/left-pad.json?registry_uri=https://registry.npmjs.com') - .expectJSONTypes(Joi.object().keys({ name: 'npm', value: isSemver })) - -t.create('public domain license') - .get('/l/redux-auth.json?style=_shields_test') - .expectJSON({ name: 'license', value: 'WTFPL', colorB: '#7cd958' }) - -t.create('copyleft license') - .get('/l/trianglify.json?style=_shields_test') - .expectJSON({ name: 'license', value: 'GPL-3.0', colorB: colorsB.orange }) - -t.create('permissive license') - .get('/l/express.json?style=_shields_test') - .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) - -t.create('permissive license for scoped package') - .get('/l/@cycle%2Fcore.json?style=_shields_test') - .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) - -t.create( - 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)' -) - .get('/l/rho-cc-promise.json?style=_shields_test') - .expectJSON({ - name: 'license', - value: '(MPL-2.0 OR MIT)', - colorB: colorsB.lightgrey, - }) - -t.create('license for package without a license property') - .get('/l/package-without-license.json?style=_shields_test') - .intercept(nock => - nock('https://registry.npmjs.org') - .get('/package-without-license/latest') - .reply(200, { - name: 'package-without-license', - }) - ) - .expectJSON({ name: 'license', value: 'missing', colorB: colorsB.red }) - -t.create('license for package with a license object') - .get('/l/package-license-object.json?style=_shields_test') - .intercept(nock => - nock('https://registry.npmjs.org') - .get('/package-license-object/latest') - .reply(200, { - name: 'package-license-object', - license: { - type: 'MIT', - url: 'https://www.opensource.org/licenses/mit-license.php', - }, - }) - ) - .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) - -t.create('license for package with a license array') - .get('/l/package-license-array.json?style=_shields_test') - .intercept(nock => - nock('https://registry.npmjs.org') - .get('/package-license-array/latest') - .reply(200, { - name: 'package-license-object', - license: ['MPL-2.0', 'MIT'], - }) - ) - .expectJSON({ - name: 'license', - value: 'MPL-2.0, MIT', - colorB: colorsB.lightgrey, - }) - -t.create('license for unknown package') - .get('/l/npm-registry-does-not-have-this-package.json?style=_shields_test') - .expectJSON({ - name: 'license', - value: 'package not found', - colorB: colorsB.red, - }) - -t.create('license when network is off') - .get('/l/pakage-network-off.json?style=_shields_test') - .networkOff() - .expectJSON({ - name: 'license', - value: 'inaccessible', - colorB: colorsB.lightgrey, - }) - -// This tests error-handling functionality in NpmBase. -t.create('when json is malformed for scoped package') - .get('/l/@cycle%2Fcore.json') - .intercept(nock => - nock('https://registry.npmjs.org') - .get('/@cycle%2Fcore') - .reply(200, { - 'dist-tags': { - latest: '1.2.3', - }, - versions: null, - }) - ) - .expectJSON({ - name: 'license', - value: 'invalid json response', - }) - -t.create('types') - .get('/types/chalk.json') - .expectJSONTypes( - Joi.object().keys({ name: 'type definitions', value: isTypeDefinition }) - ) - -t.create('no types') - .get('/types/left-pad.json') - .expectJSON({ name: 'type definitions', value: 'none' }) From 956f27fd8af1c9e0fd5be6de44379741d416a79a Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 14:49:11 -0400 Subject: [PATCH 03/13] Split up uptimerobot --- .../uptimerobot/uptimerobot-ratio.tester.js | 77 ++++++++++ .../uptimerobot/uptimerobot-status.tester.js | 74 +++++++++ services/uptimerobot/uptimerobot.tester.js | 142 ------------------ 3 files changed, 151 insertions(+), 142 deletions(-) create mode 100644 services/uptimerobot/uptimerobot-ratio.tester.js create mode 100644 services/uptimerobot/uptimerobot-status.tester.js delete mode 100644 services/uptimerobot/uptimerobot.tester.js diff --git a/services/uptimerobot/uptimerobot-ratio.tester.js b/services/uptimerobot/uptimerobot-ratio.tester.js new file mode 100644 index 0000000000000..3e4b3a45eb6d5 --- /dev/null +++ b/services/uptimerobot/uptimerobot-ratio.tester.js @@ -0,0 +1,77 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const { isPercentage } = require('../test-validators') +const { invalidJSON } = require('../response-fixtures') + +const t = ServiceTester.forThisService() +module.exports = t + +t.create('Uptime Robot: Percentage (valid)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'uptime', + value: isPercentage, + }) + ) + +t.create('Uptime Robot: Percentage (valid, with numberOfDays param)') + .get('/7/m778918918-3e92c097147760ee39d02d36.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'uptime', + value: isPercentage, + }) + ) + +t.create('Uptime Robot: Percentage (invalid, correct format)') + .get('/m777777777-333333333333333333333333.json') + .expectJSON({ name: 'uptime', value: 'api_key not found.' }) + +t.create('Uptime Robot: Percentage (invalid, incorrect format)') + .get('/not-a-service.json') + .expectJSON({ name: 'uptime', value: 'must use a monitor-specific api key' }) + +t.create('Uptime Robot: Percentage (unspecified error)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(200, '{"stat": "fail"}') + ) + .expectJSON({ name: 'uptime', value: 'service error' }) + +t.create('Uptime Robot: Percentage (connection error)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .networkOff() + .expectJSON({ name: 'uptime', value: 'inaccessible' }) + +t.create('Uptime Robot: Percentage (service unavailable)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(503, '{"error": "oh noes!!"}') + ) + .expectJSON({ name: 'uptime', value: 'inaccessible' }) + +t.create('Uptime Robot: Percentage (unexpected response, valid json)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(200, '[]') + ) + .expectJSON({ name: 'uptime', value: 'invalid json response' }) + +t.create('Uptime Robot: Percentage (unexpected response, invalid json)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(invalidJSON) + ) + .expectJSON({ name: 'uptime', value: 'unparseable json response' }) diff --git a/services/uptimerobot/uptimerobot-status.tester.js b/services/uptimerobot/uptimerobot-status.tester.js new file mode 100644 index 0000000000000..85daab70123fe --- /dev/null +++ b/services/uptimerobot/uptimerobot-status.tester.js @@ -0,0 +1,74 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const isUptimeStatus = Joi.string().valid( + 'paused', + 'not checked yet', + 'up', + 'seems down', + 'down' +) +const { invalidJSON } = require('../response-fixtures') + +const t = ServiceTester.forThisService() +module.exports = t + +t.create('Uptime Robot: Status (valid)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'status', + value: isUptimeStatus, + }) + ) + +t.create('Uptime Robot: Status (invalid, correct format)') + .get('/m777777777-333333333333333333333333.json') + .expectJSON({ name: 'status', value: 'api_key not found.' }) + +t.create('Uptime Robot: Status (invalid, incorrect format)') + .get('/not-a-service.json') + .expectJSON({ name: 'status', value: 'must use a monitor-specific api key' }) + +t.create('Uptime Robot: Status (unspecified error)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(200, '{"stat": "fail"}') + ) + .expectJSON({ name: 'status', value: 'service error' }) + +t.create('Uptime Robot: Status (connection error)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .networkOff() + .expectJSON({ name: 'status', value: 'inaccessible' }) + +t.create('Uptime Robot: Status (service unavailable)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(503, '{"error": "oh noes!!"}') + ) + .expectJSON({ name: 'status', value: 'inaccessible' }) + +t.create('Uptime Robot: Status (unexpected response, valid json)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(200, '[]') + ) + .expectJSON({ name: 'status', value: 'invalid json response' }) + +t.create('Uptime Robot: Status (unexpected response, invalid json)') + .get('/m778918918-3e92c097147760ee39d02d36.json') + .intercept(nock => + nock('https://api.uptimerobot.com') + .post('/v2/getMonitors') + .reply(invalidJSON) + ) + .expectJSON({ name: 'status', value: 'unparseable json response' }) diff --git a/services/uptimerobot/uptimerobot.tester.js b/services/uptimerobot/uptimerobot.tester.js deleted file mode 100644 index cce9f1a383923..0000000000000 --- a/services/uptimerobot/uptimerobot.tester.js +++ /dev/null @@ -1,142 +0,0 @@ -'use strict' - -const Joi = require('joi') -const ServiceTester = require('../service-tester') - -const isUptimeStatus = Joi.string().valid( - 'paused', - 'not checked yet', - 'up', - 'seems down', - 'down' -) -const { isPercentage } = require('../test-validators') -const { invalidJSON } = require('../response-fixtures') - -const t = new ServiceTester({ id: 'uptimerobot', title: 'Uptime Robot' }) -module.exports = t - -t.create('Uptime Robot: Status (valid)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'status', - value: isUptimeStatus, - }) - ) - -t.create('Uptime Robot: Status (invalid, correct format)') - .get('/status/m777777777-333333333333333333333333.json') - .expectJSON({ name: 'status', value: 'api_key not found.' }) - -t.create('Uptime Robot: Status (invalid, incorrect format)') - .get('/status/not-a-service.json') - .expectJSON({ name: 'status', value: 'must use a monitor-specific api key' }) - -t.create('Uptime Robot: Status (unspecified error)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(200, '{"stat": "fail"}') - ) - .expectJSON({ name: 'status', value: 'service error' }) - -t.create('Uptime Robot: Status (connection error)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .networkOff() - .expectJSON({ name: 'status', value: 'inaccessible' }) - -t.create('Uptime Robot: Status (service unavailable)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(503, '{"error": "oh noes!!"}') - ) - .expectJSON({ name: 'status', value: 'inaccessible' }) - -t.create('Uptime Robot: Status (unexpected response, valid json)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(200, '[]') - ) - .expectJSON({ name: 'status', value: 'invalid json response' }) - -t.create('Uptime Robot: Status (unexpected response, invalid json)') - .get('/status/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(invalidJSON) - ) - .expectJSON({ name: 'status', value: 'unparseable json response' }) - -t.create('Uptime Robot: Percentage (valid)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'uptime', - value: isPercentage, - }) - ) - -t.create('Uptime Robot: Percentage (valid, with numberOfDays param)') - .get('/ratio/7/m778918918-3e92c097147760ee39d02d36.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'uptime', - value: isPercentage, - }) - ) - -t.create('Uptime Robot: Percentage (invalid, correct format)') - .get('/ratio/m777777777-333333333333333333333333.json') - .expectJSON({ name: 'uptime', value: 'api_key not found.' }) - -t.create('Uptime Robot: Percentage (invalid, incorrect format)') - .get('/ratio/not-a-service.json') - .expectJSON({ name: 'uptime', value: 'must use a monitor-specific api key' }) - -t.create('Uptime Robot: Percentage (unspecified error)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(200, '{"stat": "fail"}') - ) - .expectJSON({ name: 'uptime', value: 'service error' }) - -t.create('Uptime Robot: Percentage (connection error)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .networkOff() - .expectJSON({ name: 'uptime', value: 'inaccessible' }) - -t.create('Uptime Robot: Percentage (service unavailable)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(503, '{"error": "oh noes!!"}') - ) - .expectJSON({ name: 'uptime', value: 'inaccessible' }) - -t.create('Uptime Robot: Percentage (unexpected response, valid json)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(200, '[]') - ) - .expectJSON({ name: 'uptime', value: 'invalid json response' }) - -t.create('Uptime Robot: Percentage (unexpected response, invalid json)') - .get('/ratio/m778918918-3e92c097147760ee39d02d36.json') - .intercept(nock => - nock('https://api.uptimerobot.com') - .post('/v2/getMonitors') - .reply(invalidJSON) - ) - .expectJSON({ name: 'uptime', value: 'unparseable json response' }) From 4e6d35d007ac24b180734055019597cdca42959e Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 14:49:16 -0400 Subject: [PATCH 04/13] Run tests by prefix --- lib/service-test-runner/runner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/service-test-runner/runner.js b/lib/service-test-runner/runner.js index 59fdf93bd3c02..ecffe0a7d960e 100644 --- a/lib/service-test-runner/runner.js +++ b/lib/service-test-runner/runner.js @@ -25,13 +25,13 @@ class Runner { } _testersForService(service) { - return this.testers.filter(t => t.id.toLowerCase() === service) + return this.testers.filter(t => t.id.toLowerCase().startsWith(service)) } /** * Limit the test run to the specified services. * - * @param services An array of service ids to run + * @param services An array of service id prefixes to run */ only(services) { const normalizedServices = new Set(services.map(v => v.toLowerCase())) From 6ae8f8818858cf6090b6fe727dbd098015bd5b31 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 15:08:02 -0400 Subject: [PATCH 05/13] Re-update package-lock to try to fix build --- package-lock.json | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index efcc3a810be52..8dab8f02da457 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2217,11 +2217,26 @@ "integrity": "sha1-uFGGD3Dhlds9J3OVqhp+I+ow7PU=", "dev": true }, + "caller-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", + "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", + "dev": true, + "requires": { + "callsites": "^0.2.0" + } + }, "callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" }, + "callsites": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", + "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", + "dev": true + }, "camelcase": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", @@ -12386,23 +12401,6 @@ "requires": { "caller-path": "^0.1.0", "resolve-from": "^1.0.0" - }, - "dependencies": { - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "^0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - } } }, "resolve": { From 460843b04e00dd92c75dbcde82637d9d01db4ede Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 15:30:38 -0400 Subject: [PATCH 06/13] Run prettier --- services/npm/npm-license.tester.js | 43 +++++++++++------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/services/npm/npm-license.tester.js b/services/npm/npm-license.tester.js index df048b8ecd712..0547f52568d23 100644 --- a/services/npm/npm-license.tester.js +++ b/services/npm/npm-license.tester.js @@ -9,40 +9,33 @@ const t = ServiceTester.forThisService() module.exports = t const colorsB = mapValues(colorscheme, 'colorB') -t - .create('gets the license of express') +t.create('gets the license of express') .get('/express.json') .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) -t - .create('gets the license of express from a custom registry') +t.create('gets the license of express from a custom registry') .get('/express.json?registry_uri=https://registry.npmjs.com') .expectJSONTypes(Joi.object().keys({ name: 'license', value: 'MIT' })) -t - .create('public domain license') +t.create('public domain license') .get('/redux-auth.json?style=_shields_test') .expectJSON({ name: 'license', value: 'WTFPL', colorB: '#7cd958' }) -t - .create('copyleft license') +t.create('copyleft license') .get('/trianglify.json?style=_shields_test') .expectJSON({ name: 'license', value: 'GPL-3.0', colorB: colorsB.orange }) -t - .create('permissive license') +t.create('permissive license') .get('/express.json?style=_shields_test') .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) -t - .create('permissive license for scoped package') +t.create('permissive license for scoped package') .get('/@cycle%2Fcore.json?style=_shields_test') .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) -t - .create( - 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)' - ) +t.create( + 'permissive and copyleft licenses (SPDX license expression syntax version 2.0)' +) .get('/rho-cc-promise.json?style=_shields_test') .expectJSON({ name: 'license', @@ -50,8 +43,7 @@ t colorB: colorsB.lightgrey, }) -t - .create('license for package without a license property') +t.create('license for package without a license property') .get('/package-without-license.json?style=_shields_test') .intercept(nock => nock('https://registry.npmjs.org') @@ -62,8 +54,7 @@ t ) .expectJSON({ name: 'license', value: 'missing', colorB: colorsB.red }) -t - .create('license for package with a license object') +t.create('license for package with a license object') .get('/package-license-object.json?style=_shields_test') .intercept(nock => nock('https://registry.npmjs.org') @@ -78,8 +69,7 @@ t ) .expectJSON({ name: 'license', value: 'MIT', colorB: colorsB.green }) -t - .create('license for package with a license array') +t.create('license for package with a license array') .get('/package-license-array.json?style=_shields_test') .intercept(nock => nock('https://registry.npmjs.org') @@ -95,8 +85,7 @@ t colorB: colorsB.lightgrey, }) -t - .create('license for unknown package') +t.create('license for unknown package') .get('/npm-registry-does-not-have-this-package.json?style=_shields_test') .expectJSON({ name: 'license', @@ -104,8 +93,7 @@ t colorB: colorsB.red, }) -t - .create('license when network is off') +t.create('license when network is off') .get('/pakage-network-off.json?style=_shields_test') .networkOff() .expectJSON({ @@ -115,8 +103,7 @@ t }) // This tests error-handling functionality in NpmBase. -t - .create('when json is malformed for scoped package') +t.create('when json is malformed for scoped package') .get('/@cycle%2Fcore.json') .intercept(nock => nock('https://registry.npmjs.org') From 842ae29223eeb8290ff6447bd6e77fbcdfb4fc05 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Fri, 17 Aug 2018 17:26:48 -0400 Subject: [PATCH 07/13] Reset --- lib/service-test-runner/runner.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/service-test-runner/runner.js b/lib/service-test-runner/runner.js index ecffe0a7d960e..59fdf93bd3c02 100644 --- a/lib/service-test-runner/runner.js +++ b/lib/service-test-runner/runner.js @@ -25,13 +25,13 @@ class Runner { } _testersForService(service) { - return this.testers.filter(t => t.id.toLowerCase().startsWith(service)) + return this.testers.filter(t => t.id.toLowerCase() === service) } /** * Limit the test run to the specified services. * - * @param services An array of service id prefixes to run + * @param services An array of service ids to run */ only(services) { const normalizedServices = new Set(services.map(v => v.toLowerCase())) From 36450264b143ccc5a70cd57c5890738e80a2d084 Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Tue, 21 Aug 2018 23:02:35 -0400 Subject: [PATCH 08/13] Use for clojars --- .../{clojars.service.js => clojars-version.service.js} | 0 .../{clojars.tester.js => clojars-version.tester.js} | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) rename services/clojars/{clojars.service.js => clojars-version.service.js} (100%) rename services/clojars/{clojars.tester.js => clojars-version.tester.js} (76%) diff --git a/services/clojars/clojars.service.js b/services/clojars/clojars-version.service.js similarity index 100% rename from services/clojars/clojars.service.js rename to services/clojars/clojars-version.service.js diff --git a/services/clojars/clojars.tester.js b/services/clojars/clojars-version.tester.js similarity index 76% rename from services/clojars/clojars.tester.js rename to services/clojars/clojars-version.tester.js index 034adddc11c63..5dab0e0d135f6 100644 --- a/services/clojars/clojars.tester.js +++ b/services/clojars/clojars-version.tester.js @@ -3,11 +3,11 @@ const Joi = require('joi') const ServiceTester = require('../service-tester') -const t = new ServiceTester({ id: 'clojars', title: 'clojars' }) +const t = ServiceTester.forThisService() module.exports = t t.create('clojars (valid)') - .get('/v/prismic.json') + .get('/prismic.json') .expectJSONTypes( Joi.object().keys({ name: 'clojars', @@ -16,5 +16,5 @@ t.create('clojars (valid)') ) t.create('clojars (not found)') - .get('/v/not-a-package.json') + .get('/not-a-package.json') .expectJSON({ name: 'clojars', value: 'not found' }) From 60c5bb3628d19a76b62a56e53702df84563bb4aa Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Tue, 21 Aug 2018 23:05:13 -0400 Subject: [PATCH 09/13] Use for appveyor --- services/appveyor/appveyor-ci.tester.js | 29 ++++++++++++ services/appveyor/appveyor-tests.tester.js | 30 +++++++++++++ services/appveyor/appveyor.tester.js | 51 ---------------------- 3 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 services/appveyor/appveyor-ci.tester.js create mode 100644 services/appveyor/appveyor-tests.tester.js delete mode 100644 services/appveyor/appveyor.tester.js diff --git a/services/appveyor/appveyor-ci.tester.js b/services/appveyor/appveyor-ci.tester.js new file mode 100644 index 0000000000000..11d1a4bcb8ca1 --- /dev/null +++ b/services/appveyor/appveyor-ci.tester.js @@ -0,0 +1,29 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const { isBuildStatus } = require('../test-validators') + +const t = ServiceTester.forThisService() +module.exports = t + +// Test AppVeyor build status badge +t.create('CI build status') + .get('/gruntjs/grunt.json') + .expectJSONTypes(Joi.object().keys({ name: 'build', value: isBuildStatus })) + +// Test AppVeyor branch build status badge +t.create('CI build status on master branch') + .get('/gruntjs/grunt/master.json') + .expectJSONTypes(Joi.object().keys({ name: 'build', value: isBuildStatus })) + +// Test AppVeyor build status badge on a non-existing project +t.create('CI 404') + .get('/somerandomproject/thatdoesntexits.json') + .expectJSON({ name: 'build', value: 'project not found or access denied' }) + +t.create('CI (connection error)') + .get('/this-one/is-not-real-either.json') + .networkOff() + .expectJSON({ name: 'build', value: 'inaccessible' }) diff --git a/services/appveyor/appveyor-tests.tester.js b/services/appveyor/appveyor-tests.tester.js new file mode 100644 index 0000000000000..eccfe7e5b4de6 --- /dev/null +++ b/services/appveyor/appveyor-tests.tester.js @@ -0,0 +1,30 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const isAppveyorTestTotals = Joi.string().regex( + /^(?:[0-9]+ (?:passed|skipped|failed)(?:, )?)+$/ +) + +const t = ServiceTester.forThisService() +module.exports = t + +// Test AppVeyor tests status badge +t.create('tests status') + .get('/NZSmartie/coap-net-iu0to.json') + .expectJSONTypes( + Joi.object().keys({ name: 'tests', value: isAppveyorTestTotals }) + ) + +// Test AppVeyor branch tests status badge +t.create('tests status on master branch') + .get('/NZSmartie/coap-net-iu0to/master.json') + .expectJSONTypes( + Joi.object().keys({ name: 'tests', value: isAppveyorTestTotals }) + ) + +// Test AppVeyor tests status badge for a non-existing project +t.create('tests 404') + .get('/somerandomproject/thatdoesntexits.json') + .expectJSON({ name: 'tests', value: 'project not found or access denied' }) diff --git a/services/appveyor/appveyor.tester.js b/services/appveyor/appveyor.tester.js deleted file mode 100644 index 7be77fd216838..0000000000000 --- a/services/appveyor/appveyor.tester.js +++ /dev/null @@ -1,51 +0,0 @@ -'use strict' - -const Joi = require('joi') -const ServiceTester = require('../service-tester') - -const { isBuildStatus } = require('../test-validators') -const isAppveyorTestTotals = Joi.string().regex( - /^(?:[0-9]+ (?:passed|skipped|failed)(?:, )?)+$/ -) - -const t = new ServiceTester({ id: 'appveyor', title: 'AppVeyor' }) -module.exports = t - -// Test AppVeyor build status badge -t.create('CI build status') - .get('/ci/gruntjs/grunt.json') - .expectJSONTypes(Joi.object().keys({ name: 'build', value: isBuildStatus })) - -// Test AppVeyor branch build status badge -t.create('CI build status on master branch') - .get('/ci/gruntjs/grunt/master.json') - .expectJSONTypes(Joi.object().keys({ name: 'build', value: isBuildStatus })) - -// Test AppVeyor build status badge on a non-existing project -t.create('CI 404') - .get('/ci/somerandomproject/thatdoesntexits.json') - .expectJSON({ name: 'build', value: 'project not found or access denied' }) - -t.create('CI (connection error)') - .get('/ci/this-one/is-not-real-either.json') - .networkOff() - .expectJSON({ name: 'build', value: 'inaccessible' }) - -// Test AppVeyor tests status badge -t.create('tests status') - .get('/tests/NZSmartie/coap-net-iu0to.json') - .expectJSONTypes( - Joi.object().keys({ name: 'tests', value: isAppveyorTestTotals }) - ) - -// Test AppVeyor branch tests status badge -t.create('tests status on master branch') - .get('/tests/NZSmartie/coap-net-iu0to/master.json') - .expectJSONTypes( - Joi.object().keys({ name: 'tests', value: isAppveyorTestTotals }) - ) - -// Test AppVeyor tests status badge for a non-existing project -t.create('tests 404') - .get('/tests/somerandomproject/thatdoesntexits.json') - .expectJSON({ name: 'tests', value: 'project not found or access denied' }) From 61f2163c5f3eca8fb339ee960c801ed34216342d Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Tue, 21 Aug 2018 23:11:38 -0400 Subject: [PATCH 10/13] Use for gem --- ...{gem.tester.js => gem-downloads.tester.js} | 71 ++----------------- services/gem/gem-owner.tester.js | 22 ++++++ services/gem/gem-rank.tester.js | 34 +++++++++ services/gem/gem-version.tester.js | 22 ++++++ 4 files changed, 84 insertions(+), 65 deletions(-) rename services/gem/{gem.tester.js => gem-downloads.tester.js} (52%) create mode 100644 services/gem/gem-owner.tester.js create mode 100644 services/gem/gem-rank.tester.js create mode 100644 services/gem/gem-version.tester.js diff --git a/services/gem/gem.tester.js b/services/gem/gem-downloads.tester.js similarity index 52% rename from services/gem/gem.tester.js rename to services/gem/gem-downloads.tester.js index b2e2413d2a43b..fc749adb79275 100644 --- a/services/gem/gem.tester.js +++ b/services/gem/gem-downloads.tester.js @@ -3,35 +3,15 @@ const Joi = require('joi') const ServiceTester = require('../service-tester') -const { - isVPlusDottedVersionAtLeastOne, - isMetric, -} = require('../test-validators') -const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/) -const isOrdinalNumberDaily = Joi.string().regex( - /^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/ -) +const { isMetric } = require('../test-validators') -const t = new ServiceTester({ id: 'gem', title: 'Ruby Gems' }) +const t = new ServiceTester({ + id: 'gem-downloads', + title: 'Ruby Gem Downloads', + pathPrefix: '/gem', +}) module.exports = t -// version endpoint - -t.create('version (valid)') - .get('/v/formatador.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'gem', - value: isVPlusDottedVersionAtLeastOne, - }) - ) - -t.create('version (not found)') - .get('/v/not-a-package.json') - .expectJSON({ name: 'gem', value: 'not found' }) - -// downloads endpoints - // total downloads t.create('total downloads (valid)') .get('/dt/rails.json') @@ -90,42 +70,3 @@ t.create('latest version downloads (valid)') t.create('latest version downloads (not found)') .get('/dtv/not-a-package.json') .expectJSON({ name: 'downloads', value: 'not found' }) - -// users endpoint - -t.create('users (valid)') - .get('/u/raphink.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'gems', - value: Joi.string().regex(/^[0-9]+$/), - }) - ) - -t.create('users (not found)') - .get('/u/not-a-package.json') - .expectJSON({ name: 'gems', value: 'not found' }) - -// rank endpoint - -t.create('total rank (valid)') - .get('/rt/rspec-puppet-facts.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'rank', - value: isOrdinalNumber, - }) - ) - -t.create('daily rank (valid)') - .get('/rd/rspec-puppet-facts.json') - .expectJSONTypes( - Joi.object().keys({ - name: 'rank', - value: isOrdinalNumberDaily, - }) - ) - -t.create('rank (not found)') - .get('/rt/not-a-package.json') - .expectJSON({ name: 'rank', value: 'not found' }) diff --git a/services/gem/gem-owner.tester.js b/services/gem/gem-owner.tester.js new file mode 100644 index 0000000000000..64c22f94e02a4 --- /dev/null +++ b/services/gem/gem-owner.tester.js @@ -0,0 +1,22 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const t = ServiceTester.forThisService() +module.exports = t + +t + .create('users (valid)') + .get('/u/raphink.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'gems', + value: Joi.string().regex(/^[0-9]+$/), + }) + ) + +t + .create('users (not found)') + .get('/u/not-a-package.json') + .expectJSON({ name: 'gems', value: 'not found' }) diff --git a/services/gem/gem-rank.tester.js b/services/gem/gem-rank.tester.js new file mode 100644 index 0000000000000..d357998314e15 --- /dev/null +++ b/services/gem/gem-rank.tester.js @@ -0,0 +1,34 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/) +const isOrdinalNumberDaily = Joi.string().regex( + /^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/ +) + +const t = ServiceTester.forThisService() +module.exports = t + +t.create('total rank (valid)') + .get('/rt/rspec-puppet-facts.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'rank', + value: isOrdinalNumber, + }) + ) + +t.create('daily rank (valid)') + .get('/rd/rspec-puppet-facts.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'rank', + value: isOrdinalNumberDaily, + }) + ) + +t.create('rank (not found)') + .get('/rt/not-a-package.json') + .expectJSON({ name: 'rank', value: 'not found' }) diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js new file mode 100644 index 0000000000000..b020a50aa0e4b --- /dev/null +++ b/services/gem/gem-version.tester.js @@ -0,0 +1,22 @@ +'use strict' + +const Joi = require('joi') +const ServiceTester = require('../service-tester') + +const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') + +const t = ServiceTester.forThisService() +module.exports = t + +t.create('version (valid)') + .get('/formatador.json') + .expectJSONTypes( + Joi.object().keys({ + name: 'gem', + value: isVPlusDottedVersionAtLeastOne, + }) + ) + +t.create('version (not found)') + .get('/not-a-package.json') + .expectJSON({ name: 'gem', value: 'not found' }) From 58139a6b18f3db19d8d3f88bdd942fc05d428cca Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Tue, 21 Aug 2018 23:21:12 -0400 Subject: [PATCH 11/13] Move createServiceTester into its own module --- services/appveyor/appveyor-ci.tester.js | 4 +-- services/appveyor/appveyor-tests.tester.js | 4 +-- services/clojars/clojars-version.tester.js | 4 +-- services/create-service-tester.js | 29 +++++++++++++++++++ services/gem/gem-owner.tester.js | 10 +++---- services/gem/gem-rank.tester.js | 4 +-- services/gem/gem-version.tester.js | 4 +-- .../librariesio-dependent-repos.tester.js | 4 +-- .../librariesio-dependents.tester.js | 4 +-- .../librariesio-sourcerank.tester.js | 4 +-- services/npm/npm-license.tester.js | 4 +-- services/npm/npm-type-definitions.tester.js | 4 +-- services/npm/npm-version.tester.js | 4 +-- services/service-tester.js | 18 ------------ .../uptimerobot/uptimerobot-ratio.tester.js | 4 +-- .../uptimerobot/uptimerobot-status.tester.js | 4 +-- 16 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 services/create-service-tester.js diff --git a/services/appveyor/appveyor-ci.tester.js b/services/appveyor/appveyor-ci.tester.js index 11d1a4bcb8ca1..9e79d84834993 100644 --- a/services/appveyor/appveyor-ci.tester.js +++ b/services/appveyor/appveyor-ci.tester.js @@ -1,11 +1,11 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isBuildStatus } = require('../test-validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t // Test AppVeyor build status badge diff --git a/services/appveyor/appveyor-tests.tester.js b/services/appveyor/appveyor-tests.tester.js index eccfe7e5b4de6..06803908a5347 100644 --- a/services/appveyor/appveyor-tests.tester.js +++ b/services/appveyor/appveyor-tests.tester.js @@ -1,13 +1,13 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const isAppveyorTestTotals = Joi.string().regex( /^(?:[0-9]+ (?:passed|skipped|failed)(?:, )?)+$/ ) -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t // Test AppVeyor tests status badge diff --git a/services/clojars/clojars-version.tester.js b/services/clojars/clojars-version.tester.js index 5dab0e0d135f6..5abe4d5ad1c7e 100644 --- a/services/clojars/clojars-version.tester.js +++ b/services/clojars/clojars-version.tester.js @@ -1,9 +1,9 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('clojars (valid)') diff --git a/services/create-service-tester.js b/services/create-service-tester.js new file mode 100644 index 0000000000000..5ba69df69d5f6 --- /dev/null +++ b/services/create-service-tester.js @@ -0,0 +1,29 @@ +'use strict' + +const caller = require('caller') +const ServiceTester = require('./service-tester') +const BaseService = require('./base') + +// Automatically create a ServiceTester. When run from e.g. +// `gem-rank.tester.js`, this will create a tester that attaches to the +// service found in `gem-rank.service.js`. +// +// This can't be used for `.service.js` files which export more than one +// service. +function createServiceTester() { + const servicePath = caller().replace('.tester.js', '.service.js') + let ServiceClass + try { + ServiceClass = require(servicePath) + } catch (e) { + throw Error(`Couldn't load service from ${servicePath}`) + } + if (!(ServiceClass.prototype instanceof BaseService)) { + throw Error( + `${servicePath} does not export a single service. Invoke new ServiceTester() directly.` + ) + } + return ServiceTester.forServiceClass(ServiceClass) +} + +module.exports = createServiceTester diff --git a/services/gem/gem-owner.tester.js b/services/gem/gem-owner.tester.js index 64c22f94e02a4..bc0bc783bf2d4 100644 --- a/services/gem/gem-owner.tester.js +++ b/services/gem/gem-owner.tester.js @@ -1,13 +1,12 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t -t - .create('users (valid)') +t.create('users (valid)') .get('/u/raphink.json') .expectJSONTypes( Joi.object().keys({ @@ -16,7 +15,6 @@ t }) ) -t - .create('users (not found)') +t.create('users (not found)') .get('/u/not-a-package.json') .expectJSON({ name: 'gems', value: 'not found' }) diff --git a/services/gem/gem-rank.tester.js b/services/gem/gem-rank.tester.js index d357998314e15..0518e4f3ea330 100644 --- a/services/gem/gem-rank.tester.js +++ b/services/gem/gem-rank.tester.js @@ -1,14 +1,14 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const isOrdinalNumber = Joi.string().regex(/^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ)$/) const isOrdinalNumberDaily = Joi.string().regex( /^[1-9][0-9]+(ᵗʰ|ˢᵗ|ⁿᵈ|ʳᵈ) daily$/ ) -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('total rank (valid)') diff --git a/services/gem/gem-version.tester.js b/services/gem/gem-version.tester.js index b020a50aa0e4b..67d6944fb2b39 100644 --- a/services/gem/gem-version.tester.js +++ b/services/gem/gem-version.tester.js @@ -1,11 +1,11 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('version (valid)') diff --git a/services/librariesio/librariesio-dependent-repos.tester.js b/services/librariesio/librariesio-dependent-repos.tester.js index 2624beb4bb045..e0c281837e57d 100644 --- a/services/librariesio/librariesio-dependent-repos.tester.js +++ b/services/librariesio/librariesio-dependent-repos.tester.js @@ -1,10 +1,10 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isMetric } = require('../test-validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('dependent repo count') diff --git a/services/librariesio/librariesio-dependents.tester.js b/services/librariesio/librariesio-dependents.tester.js index fb7ecacb35d6f..1da3504f7953b 100644 --- a/services/librariesio/librariesio-dependents.tester.js +++ b/services/librariesio/librariesio-dependents.tester.js @@ -1,10 +1,10 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isMetric } = require('../test-validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('dependent count') diff --git a/services/librariesio/librariesio-sourcerank.tester.js b/services/librariesio/librariesio-sourcerank.tester.js index b3c1600d44a39..7fa05d264eec2 100644 --- a/services/librariesio/librariesio-sourcerank.tester.js +++ b/services/librariesio/librariesio-sourcerank.tester.js @@ -1,10 +1,10 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { anyInteger } = require('../validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('sourcerank') diff --git a/services/npm/npm-license.tester.js b/services/npm/npm-license.tester.js index 0547f52568d23..ddc0f8c8bc59d 100644 --- a/services/npm/npm-license.tester.js +++ b/services/npm/npm-license.tester.js @@ -1,11 +1,11 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const colorscheme = require('../../lib/colorscheme.json') const mapValues = require('lodash.mapvalues') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t const colorsB = mapValues(colorscheme, 'colorB') diff --git a/services/npm/npm-type-definitions.tester.js b/services/npm/npm-type-definitions.tester.js index 0c16403ea9c4b..173d56aa1a1b2 100644 --- a/services/npm/npm-type-definitions.tester.js +++ b/services/npm/npm-type-definitions.tester.js @@ -1,9 +1,9 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t const isTypeDefinition = Joi.string().regex( diff --git a/services/npm/npm-version.tester.js b/services/npm/npm-version.tester.js index 6f635097d43d3..d2679339d354a 100644 --- a/services/npm/npm-version.tester.js +++ b/services/npm/npm-version.tester.js @@ -1,10 +1,10 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isSemver } = require('../test-validators') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('gets the package version of left-pad') diff --git a/services/service-tester.js b/services/service-tester.js index b7f7da2d1e437..91223be910b4b 100644 --- a/services/service-tester.js +++ b/services/service-tester.js @@ -1,10 +1,8 @@ 'use strict' const frisby = require('icedfrisby-nock')(require('icedfrisby')) -const caller = require('caller') const emojic = require('emojic') const config = require('../lib/test-config') -const BaseService = require('./base') const trace = require('./trace') /** @@ -41,22 +39,6 @@ class ServiceTester { }) } - static forThisService() { - const servicePath = caller().replace('.tester.js', '.service.js') - let ServiceClass - try { - ServiceClass = require(servicePath) - } catch (e) { - throw Error(`Couldn't load service from ${servicePath}`) - } - if (!(ServiceClass.prototype instanceof BaseService)) { - throw Error( - `${servicePath} does not export a single service. Invoke new ServiceTester() directly.` - ) - } - return this.forServiceClass(ServiceClass) - } - /** * Invoked before each test. This is a stub which can be overridden on * instances. diff --git a/services/uptimerobot/uptimerobot-ratio.tester.js b/services/uptimerobot/uptimerobot-ratio.tester.js index 3e4b3a45eb6d5..db7bb8b778162 100644 --- a/services/uptimerobot/uptimerobot-ratio.tester.js +++ b/services/uptimerobot/uptimerobot-ratio.tester.js @@ -1,12 +1,12 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const { isPercentage } = require('../test-validators') const { invalidJSON } = require('../response-fixtures') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('Uptime Robot: Percentage (valid)') diff --git a/services/uptimerobot/uptimerobot-status.tester.js b/services/uptimerobot/uptimerobot-status.tester.js index 85daab70123fe..d2592fbbfed3d 100644 --- a/services/uptimerobot/uptimerobot-status.tester.js +++ b/services/uptimerobot/uptimerobot-status.tester.js @@ -1,7 +1,7 @@ 'use strict' const Joi = require('joi') -const ServiceTester = require('../service-tester') +const createServiceTester = require('../create-service-tester') const isUptimeStatus = Joi.string().valid( 'paused', @@ -12,7 +12,7 @@ const isUptimeStatus = Joi.string().valid( ) const { invalidJSON } = require('../response-fixtures') -const t = ServiceTester.forThisService() +const t = createServiceTester() module.exports = t t.create('Uptime Robot: Status (valid)') From b0fe53c300832ae39165c40d353a5d94cbe2387a Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Wed, 22 Aug 2018 15:01:26 -0400 Subject: [PATCH 12/13] Fix gem-owner test --- services/gem/gem-owner.tester.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/gem/gem-owner.tester.js b/services/gem/gem-owner.tester.js index bc0bc783bf2d4..c3aa6e0b197a9 100644 --- a/services/gem/gem-owner.tester.js +++ b/services/gem/gem-owner.tester.js @@ -7,7 +7,7 @@ const t = createServiceTester() module.exports = t t.create('users (valid)') - .get('/u/raphink.json') + .get('/raphink.json') .expectJSONTypes( Joi.object().keys({ name: 'gems', @@ -16,5 +16,5 @@ t.create('users (valid)') ) t.create('users (not found)') - .get('/u/not-a-package.json') + .get('/not-a-package.json') .expectJSON({ name: 'gems', value: 'not found' }) From 98f2c4f0a9c710fbcbb76a806da5e69861a15e6f Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Wed, 22 Aug 2018 15:01:59 -0400 Subject: [PATCH 13/13] Fix npm type definitions --- services/npm/npm-type-definitions.tester.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/npm/npm-type-definitions.tester.js b/services/npm/npm-type-definitions.tester.js index 173d56aa1a1b2..afb3fee0429ae 100644 --- a/services/npm/npm-type-definitions.tester.js +++ b/services/npm/npm-type-definitions.tester.js @@ -11,23 +11,23 @@ const isTypeDefinition = Joi.string().regex( ) t.create('types (from dev dependencies + files)') - .get('/types/chalk.json') + .get('/chalk.json') .expectJSONTypes( Joi.object().keys({ name: 'types', value: isTypeDefinition }) ) t.create('types (from files)') - .get('/types/form-data-entries.json') + .get('/form-data-entries.json') .expectJSONTypes( Joi.object().keys({ name: 'types', value: isTypeDefinition }) ) t.create('types (from types key)') - .get('/types/left-pad.json') + .get('/left-pad.json') .expectJSONTypes( Joi.object().keys({ name: 'types', value: isTypeDefinition }) ) t.create('no types') - .get('/types/link-into.json') + .get('/link-into.json') .expectJSON({ name: 'types', value: 'none' })