Repository: badges/shields (js)

Issue:
Failing Scrutinizer service tests (Coverage and Quality badges)
:clock11: **When did the problem start?**
~October 3rd

<!-- Indicate when the problem started -->

:camera: **Live badge**

<!-- Provide a link to the live badge in plain text and markdown.  -->
https://img.shields.io/scrutinizer/coverage/g/filp/whoops
![](https://img.shields.io/scrutinizer/coverage/g/filp/whoops)

:wrench: **Is the live badge working?**
Yes, seems to be. Probably just an issue with the repo used as the target in the tests
<!-- Indicate whether or not the live badge is working. -->

:link: **CircleCI link**
https://circleci.com/gh/badges/daily-tests/628
<!-- Provide a link to the failing test in CircleCI. -->

:beetle: **Stack trace**

```sh
ScrutinizerCoverage [live] code coverage branch (GitHub) [ GET /g/PHPMailer/PHPMailer/master.json ] - [ GET /g/PHPMailer/PHPMailer/master.json ]
  ValidationError: message mismatch: "value" with value "invalid response data" fails to match the required pattern: /^[1-9][0-9]?%|^100%|^0%$/

ScrutinizerQuality [live] code quality branch (GitHub) [ GET /g/PHPMailer/PHPMailer/master.json ] - [ GET /g/PHPMailer/PHPMailer/master.json ]
  ValidationError: message mismatch: "value" must be a number
```

:bulb: **Possible solution**
Using a different repo for the tests should get them passing again, but since we're also getting `invalid response data` on the Coverage test we should probably double check the schema too
<!--- Optional: only if you have suggestions on a fix/reason for the bug -->

<!-- Love Shields? Please consider donating $10 to sustain our activities:
👉  https://opencollective.com/shields -->


Pull request description:
Update service tests for [Scrutinizer Microbadger jsDelivr]
Resolves #4144 

Fixes the schema used for Scrutinzer Coverage and Quality badges to handle the API response (which can include branches missing statistics) and increases the timeouts on a couple service tests for Microbadger and jsDelivr that regularly timeout during the daily test runs

Patch:
diff --git a/services/scrutinizer/scrutinizer-base.js b/services/scrutinizer/scrutinizer-base.js
index 7f1424f714..b2a7c59cab 100644
--- a/services/scrutinizer/scrutinizer-base.js
+++ b/services/scrutinizer/scrutinizer-base.js
@@ -1,6 +1,6 @@
 'use strict'
 
-const { BaseJsonService, NotFound } = require('..')
+const { BaseJsonService, NotFound, InvalidResponse } = require('..')
 
 module.exports = class ScrutinizerBase extends BaseJsonService {
   // https://scrutinizer-ci.com/docs/api/#repository-details
@@ -30,13 +30,17 @@ module.exports = class ScrutinizerBase extends BaseJsonService {
   }
 
   transformBranchInfoMetricValue({ json, branch, metric }) {
+    const branchInfo = this.transformBranchInfo({ json, wantedBranch: branch })
+    if (!branchInfo.index) {
+      throw new InvalidResponse({ prettyMessage: 'metrics missing for branch' })
+    }
     const {
       index: {
         _embedded: {
           project: { metric_values: metricValues },
         },
       },
-    } = this.transformBranchInfo({ json, wantedBranch: branch })
+    } = branchInfo
 
     return { value: metricValues[metric] }
   }
diff --git a/services/scrutinizer/scrutinizer-coverage.service.js b/services/scrutinizer/scrutinizer-coverage.service.js
index d8bdeb4445..243549b12f 100644
--- a/services/scrutinizer/scrutinizer-coverage.service.js
+++ b/services/scrutinizer/scrutinizer-coverage.service.js
@@ -19,7 +19,7 @@ const schema = Joi.object({
               }).required(),
             }).required(),
           }).required(),
-        }).required(),
+        }),
       })
     )
     .required(),
diff --git a/services/scrutinizer/scrutinizer-coverage.spec.js b/services/scrutinizer/scrutinizer-coverage.spec.js
index ffe58da93a..d16d90aef7 100644
--- a/services/scrutinizer/scrutinizer-coverage.spec.js
+++ b/services/scrutinizer/scrutinizer-coverage.spec.js
@@ -3,7 +3,7 @@
 const { expect } = require('chai')
 const { test, given } = require('sazerac')
 const [ScrutinizerCoverage] = require('./scrutinizer-coverage.service')
-const { NotFound } = require('..')
+const { InvalidResponse, NotFound } = require('..')
 
 describe('ScrutinizerCoverage', function() {
   test(ScrutinizerCoverage.render, () => {
@@ -50,5 +50,24 @@ describe('ScrutinizerCoverage', function() {
         expect(e.prettyMessage).to.equal('coverage not found')
       }
     })
+    it('throws InvalidResponse error when branch is missing statistics', function() {
+      expect(() =>
+        ScrutinizerCoverage.prototype.transform({
+          branch: 'gh-pages',
+          json: {
+            applications: {
+              master: { index: {} },
+              'gh-pages': {
+                build_status: {
+                  status: 'unknown',
+                },
+              },
+            },
+          },
+        })
+      )
+        .to.throw(InvalidResponse)
+        .with.property('prettyMessage', 'metrics missing for branch')
+    })
   })
 })
diff --git a/services/scrutinizer/scrutinizer-quality.service.js b/services/scrutinizer/scrutinizer-quality.service.js
index 74f7f4d227..514351e529 100644
--- a/services/scrutinizer/scrutinizer-quality.service.js
+++ b/services/scrutinizer/scrutinizer-quality.service.js
@@ -18,7 +18,7 @@ const schema = Joi.object({
               }).required(),
             }).required(),
           }).required(),
-        }).required(),
+        }),
       })
     )
     .required(),


Test patch:
diff --git a/services/jsdelivr/jsdelivr-hits-github.tester.js b/services/jsdelivr/jsdelivr-hits-github.tester.js
index ec8fd1e917..3c15ff5d0a 100644
--- a/services/jsdelivr/jsdelivr-hits-github.tester.js
+++ b/services/jsdelivr/jsdelivr-hits-github.tester.js
@@ -28,7 +28,7 @@ t.create('jquery/jquery hits/month')
   })
 
 t.create('jquery/jquery hits/year')
-  .timeout(10000)
+  .timeout(25000)
   .get('/hy/jquery/jquery.json')
   .expectBadge({
     label: 'jsdelivr',
diff --git a/services/jsdelivr/jsdelivr-hits-npm.tester.js b/services/jsdelivr/jsdelivr-hits-npm.tester.js
index c35fc44a9c..f1ce84c12e 100644
--- a/services/jsdelivr/jsdelivr-hits-npm.tester.js
+++ b/services/jsdelivr/jsdelivr-hits-npm.tester.js
@@ -28,7 +28,7 @@ t.create('jquery hits/month')
   })
 
 t.create('jquery hits/year')
-  .timeout(10000)
+  .timeout(25000)
   .get('/hy/ky.json')
   .expectBadge({
     label: 'jsdelivr',
diff --git a/services/microbadger/microbadger-layers.tester.js b/services/microbadger/microbadger-layers.tester.js
index 59bc15314b..d2fd1a828b 100644
--- a/services/microbadger/microbadger-layers.tester.js
+++ b/services/microbadger/microbadger-layers.tester.js
@@ -1,24 +1,22 @@
 'use strict'
 
-const Joi = require('@hapi/joi')
+const { nonNegativeInteger } = require('../validators')
 const t = (module.exports = require('../tester').createServiceTester())
 
 t.create('layers without a specified tag')
   .get('/_/alpine.json')
+  .timeout(150000)
   .expectBadge({
     label: 'layers',
-    message: Joi.number()
-      .integer()
-      .positive(),
+    message: nonNegativeInteger,
   })
 
 t.create('layers with a specified tag')
   .get('/_/alpine/2.7.json')
+  .timeout(150000)
   .expectBadge({
     label: 'layers',
-    message: Joi.number()
-      .integer()
-      .positive(),
+    message: nonNegativeInteger,
   })
 
 t.create('specified tag when repository has only one')
diff --git a/services/microbadger/microbadger-size.tester.js b/services/microbadger/microbadger-size.tester.js
index e4e84b1447..4e6613e6e7 100644
--- a/services/microbadger/microbadger-size.tester.js
+++ b/services/microbadger/microbadger-size.tester.js
@@ -5,6 +5,7 @@ const t = (module.exports = require('../tester').createServiceTester())
 
 t.create('image size without a specified tag')
   .get('/fedora/apache.json')
+  .timeout(150000)
   .expectBadge({
     label: 'image size',
     message: isFileSize,
@@ -12,6 +13,7 @@ t.create('image size without a specified tag')
 
 t.create('image size with a specified tag')
   .get('/fedora/apache/latest.json')
+  .timeout(150000)
   .expectBadge({
     label: 'image size',
     message: isFileSize,