Repository: badges/shields
Pull request #3131 (2019-03-02 03:23:58)

Refactor [Codeship]

shields-ci: 
<!--
  0 failure: 
  0 warning: 
  1 messages
  
  DangerID: danger-id-default;
-->



<table>
  <thead>
    <tr>
      <th width="50"></th>
      <th width="100%" data-danger-table="true">Messages</th>
    </tr>
  </thead>
  <tbody><tr>
      <td>:book:</td>
      <td>:sparkles: Thanks for your contribution to Shields, @calebcartwright!</td>
    </tr>
  </tbody>
</table>


<p align="right">
  Generated by :no_entry_sign: <a href="https://danger.systems/js">dangerJS</a> against 87803196f024279d7d9f5e5f2fb5dbc5d907e4cf
</p>


Patch:
diff --git a/services/build-status.js b/services/build-status.js
index efcf295f17..82c9221307 100644
--- a/services/build-status.js
+++ b/services/build-status.js
@@ -13,13 +13,14 @@ const greenStatuses = [
 
 const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
 
-const redStatuses = ['error', 'failed', 'failing']
+const redStatuses = ['error', 'failed', 'failing', 'infrastructure_failure']
 
 const otherStatuses = [
   'building',
   'canceled',
   'cancelled',
   'expired',
+  'initiated',
   'no tests',
   'not built',
   'not run',
@@ -31,6 +32,7 @@ const otherStatuses = [
   'skipped',
   'starting',
   'stopped',
+  'testing',
   'waiting',
 ]
 
diff --git a/services/build-status.spec.js b/services/build-status.spec.js
index e89b671b31..ce8f9e9169 100644
--- a/services/build-status.spec.js
+++ b/services/build-status.spec.js
@@ -58,6 +58,7 @@ test(renderBuildStatusBadge, () => {
     given({ status: 'error' }),
     given({ status: 'failed' }),
     given({ status: 'failing' }),
+    given({ status: 'infrastructure_failure' }),
   ]).assert('should be red', b => expect(b).to.include({ color: 'red' }))
 })
 
@@ -67,6 +68,7 @@ test(renderBuildStatusBadge, () => {
     given({ status: 'canceled' }),
     given({ status: 'cancelled' }),
     given({ status: 'expired' }),
+    given({ status: 'initiated' }),
     given({ status: 'no tests' }),
     given({ status: 'not built' }),
     given({ status: 'not run' }),
@@ -78,6 +80,7 @@ test(renderBuildStatusBadge, () => {
     given({ status: 'skipped' }),
     given({ status: 'starting' }),
     given({ status: 'stopped' }),
+    given({ status: 'testing' }),
     given({ status: 'waiting' }),
   ]).assert('should have undefined color', b =>
     expect(b).to.include({ color: undefined })
diff --git a/services/codeship/codeship.service.js b/services/codeship/codeship.service.js
index d7087fee76..f6f16b54bd 100644
--- a/services/codeship/codeship.service.js
+++ b/services/codeship/codeship.service.js
@@ -1,19 +1,45 @@
 'use strict'
 
-const LegacyService = require('../legacy-service')
-const { makeBadgeData: getBadgeData } = require('../../lib/badge-data')
+const Joi = require('joi')
+const { BaseSvgScrapingService } = require('..')
+const { isBuildStatus, renderBuildStatusBadge } = require('../build-status')
 
-// This legacy service should be rewritten to use e.g. BaseJsonService.
-//
-// Tips for rewriting:
-// https://github.com/badges/shields/blob/master/doc/rewriting-services.md
-//
-// Do not base new services on this code.
-module.exports = class Codeship extends LegacyService {
+const schema = Joi.object({
+  message: Joi.alternatives()
+    .try(
+      isBuildStatus,
+      Joi.equal('project not found', 'branch not found', 'ignored', 'blocked')
+    )
+    .required(),
+}).required()
+
+const pendingStatus = 'pending'
+const notBuiltStatus = 'not built'
+
+const statusMap = {
+  testing: pendingStatus,
+  waiting: pendingStatus,
+  initiated: pendingStatus,
+  stopped: notBuiltStatus,
+  ignored: notBuiltStatus,
+  blocked: notBuiltStatus,
+  infrastructure_failure: 'failed',
+}
+
+module.exports = class Codeship extends BaseSvgScrapingService {
   static get category() {
     return 'build'
   }
 
+  static get defaultBadgeData() {
+    return { label: 'build' }
+  }
+
+  static render({ status }) {
+    status = statusMap[status] || status
+    return renderBuildStatusBadge({ status })
+  }
+
   static get route() {
     return {
       base: 'codeship',
@@ -29,92 +55,32 @@ module.exports = class Codeship extends LegacyService {
         namedParams: {
           projectId: 'd6c1ddd0-16a3-0132-5f85-2e35c05e22b1',
         },
-        staticPreview: {
-          label: 'build',
-          message: 'passing',
-          color: 'brightgreen',
-        },
+        staticPreview: renderBuildStatusBadge({ status: 'passing' }),
       },
       {
         title: 'Codeship (branch)',
         pattern: ':projectId/:branch',
         namedParams: {
-          projectId: 'd6c1ddd0-16a3-0132-5f85-2e35c05e22b1',
+          projectId: '0bdb0440-3af5-0133-00ea-0ebda3a33bf6',
           branch: 'master',
         },
-        staticPreview: {
-          label: 'build',
-          message: 'passing',
-          color: 'brightgreen',
-        },
+        staticPreview: renderBuildStatusBadge({ status: 'passing' }),
       },
     ]
   }
 
-  static registerLegacyRouteHandler({ camp, cache }) {
-    camp.route(
-      /^\/codeship\/([^/]+)(?:\/(.+))?\.(svg|png|gif|jpg|json)$/,
-      cache((data, match, sendBadge, request) => {
-        const projectId = match[1] // eg, `ab123456-00c0-0123-42de-6f98765g4h32`.
-        const format = match[3]
-        const branch = match[2]
-        const options = {
-          method: 'GET',
-          uri: `https://codeship.com/projects/${projectId}/status${
-            branch != null ? `?branch=${branch}` : ''
-          }`,
-        }
-        const badgeData = getBadgeData('build', data)
-        request(options, (err, res) => {
-          if (err != null) {
-            badgeData.text[1] = 'inaccessible'
-            sendBadge(format, badgeData)
-            return
-          }
-          try {
-            const statusMatch = res.headers['content-disposition'].match(
-              /filename="status_(.+)\./
-            )
-            if (!statusMatch) {
-              badgeData.text[1] = 'unknown'
-              sendBadge(format, badgeData)
-              return
-            }
+  async fetch({ projectId, branch }) {
+    const url = `https://app.codeship.com/projects/${projectId}/status`
+    return this._requestSvg({
+      schema,
+      url,
+      options: { qs: { branch } },
+      valueMatcher: /<g id="status_2">(?:[.\s\S]*)\/><\/g><g id="([\w\s]*)"/,
+    })
+  }
 
-            switch (statusMatch[1]) {
-              case 'success':
-                badgeData.text[1] = 'passing'
-                badgeData.colorscheme = 'brightgreen'
-                break
-              case 'projectnotfound':
-                badgeData.text[1] = 'not found'
-                break
-              case 'branchnotfound':
-                badgeData.text[1] = 'branch not found'
-                break
-              case 'testing':
-              case 'waiting':
-              case 'initiated':
-                badgeData.text[1] = 'pending'
-                break
-              case 'error':
-              case 'infrastructure_failure':
-                badgeData.text[1] = 'failing'
-                badgeData.colorscheme = 'red'
-                break
-              case 'stopped':
-              case 'ignored':
-              case 'blocked':
-                badgeData.text[1] = 'not built'
-                break
-            }
-            sendBadge(format, badgeData)
-          } catch (e) {
-            badgeData.text[1] = 'invalid'
-            sendBadge(format, badgeData)
-          }
-        })
-      })
-    )
+  async handle({ projectId, branch }) {
+    const { message: status } = await this.fetch({ projectId, branch })
+    return this.constructor.render({ status })
   }
 }
diff --git a/services/codeship/codeship.spec.js b/services/codeship/codeship.spec.js
new file mode 100644
index 0000000000..5f7ca4a417
--- /dev/null
+++ b/services/codeship/codeship.spec.js
@@ -0,0 +1,23 @@
+'use strict'
+
+const { test, given } = require('sazerac')
+const Codeship = require('./codeship.service')
+
+const pending = { message: 'pending', label: undefined, color: undefined }
+const notBuilt = { message: 'not built', label: undefined, color: undefined }
+
+describe('Codeship', function() {
+  test(Codeship.render, () => {
+    given({ status: 'testing' }).expect(pending)
+    given({ status: 'waiting' }).expect(pending)
+    given({ status: 'initiated' }).expect(pending)
+    given({ status: 'stopped' }).expect(notBuilt)
+    given({ status: 'ignored' }).expect(notBuilt)
+    given({ status: 'blocked' }).expect(notBuilt)
+    given({ status: 'infrastructure_failure' }).expect({
+      message: 'failing',
+      color: 'red',
+      label: undefined,
+    })
+  })
+})


Test patch:
diff --git a/services/codeship/codeship.tester.js b/services/codeship/codeship.tester.js
index d168f9c4a7..dce57d7ee4 100644
--- a/services/codeship/codeship.tester.js
+++ b/services/codeship/codeship.tester.js
@@ -1,12 +1,7 @@
 'use strict'
 
 const { isBuildStatus } = require('../build-status')
-const { ServiceTester } = require('../tester')
-
-const t = (module.exports = new ServiceTester({
-  id: 'codeship',
-  title: 'codeship',
-}))
+const t = (module.exports = require('../tester').createServiceTester())
 
 t.create('codeship (valid, no branch)')
   .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1.json')
@@ -16,7 +11,7 @@ t.create('codeship (valid, no branch)')
   })
 
 t.create('codeship (valid, with branch)')
-  .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/master.json')
+  .get('/0bdb0440-3af5-0133-00ea-0ebda3a33bf6/master.json')
   .expectBadge({
     label: 'build',
     message: isBuildStatus,
@@ -24,33 +19,8 @@ t.create('codeship (valid, with branch)')
 
 t.create('codeship (repo not found)')
   .get('/not-a-repo.json')
-  .expectBadge({ label: 'build', message: 'not found' })
+  .expectBadge({ label: 'build', message: 'project not found' })
 
 t.create('codeship (branch not found)')
   .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/not-a-branch.json')
   .expectBadge({ label: 'build', message: 'branch not found' })
-
-t.create('codeship (connection error)')
-  .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1.json')
-  .networkOff()
-  .expectBadge({ label: 'build', message: 'inaccessible' })
-
-t.create('codeship (unexpected response header)')
-  .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1.json')
-  .intercept(nock =>
-    nock('https://codeship.com')
-      .get('/projects/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/status')
-      .reply(200, '', {
-        'content-disposition': 'foo',
-      })
-  )
-  .expectBadge({ label: 'build', message: 'unknown' })
-
-t.create('codeship (unexpected response body)')
-  .get('/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1.json')
-  .intercept(nock =>
-    nock('https://codeship.com')
-      .get('/projects/d6c1ddd0-16a3-0132-5f85-2e35c05e22b1/status')
-      .reply(200, '')
-  )
-  .expectBadge({ label: 'build', message: 'invalid' })