Refactor [SymfonyInsight] to new service model and rename (#2572)
Based on some discussion/feedback here, this PR now contains several changes: * Renames the `Sensiolabs` badge/service content to `SymfonyInsight` to reflect the rebranding of that product/service * Refactors the original service to the new service model (using `BaseXmlService`) * Updates the color scheme of the original/initial badge type (SymfonyInsight Grade) to more closely mirror the colors used by the vendor/service provider * Adds a new badge type (violation counts/summary) * Adds both mocked and live tests (there were none before) for both the grade & violation badges using the new path `symfony/i` as well as a couple tests for the old path `sensiolabs/i` to check for backwards compatibility Refs #1358
This commit is contained in:
committed by
Paul Melnikow
parent
b32f6eab55
commit
ca487ae086
@@ -1,110 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const LegacyService = require('../legacy-service')
|
||||
const { makeBadgeData: getBadgeData } = require('../../lib/badge-data')
|
||||
const serverSecrets = require('../../lib/server-secrets')
|
||||
|
||||
// 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 Sensiolabs extends LegacyService {
|
||||
static get category() {
|
||||
return 'build'
|
||||
}
|
||||
|
||||
static get route() {
|
||||
return {
|
||||
base: 'sensiolabs',
|
||||
}
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
return [
|
||||
{
|
||||
title: 'SensioLabs Insight',
|
||||
previewUrl: 'i/45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
static registerLegacyRouteHandler({ camp, cache }) {
|
||||
camp.route(
|
||||
/^\/sensiolabs\/i\/([^/]+)\.(svg|png|gif|jpg|json)$/,
|
||||
cache((data, match, sendBadge, request) => {
|
||||
const projectUuid = match[1]
|
||||
const format = match[2]
|
||||
const options = {
|
||||
method: 'GET',
|
||||
uri: `https://insight.sensiolabs.com/api/projects/${projectUuid}`,
|
||||
headers: {
|
||||
Accept: 'application/vnd.com.sensiolabs.insight+xml',
|
||||
},
|
||||
}
|
||||
|
||||
if (serverSecrets.sl_insight_userUuid) {
|
||||
options.auth = {
|
||||
user: serverSecrets.sl_insight_userUuid,
|
||||
pass: serverSecrets.sl_insight_apiToken,
|
||||
}
|
||||
}
|
||||
|
||||
const badgeData = getBadgeData('check', data)
|
||||
|
||||
request(options, (err, res, body) => {
|
||||
if (err != null || res.statusCode !== 200) {
|
||||
badgeData.text[1] = 'inaccessible'
|
||||
sendBadge(format, badgeData)
|
||||
return
|
||||
}
|
||||
|
||||
const matchStatus = body.match(
|
||||
/<status><!\[CDATA\[([a-z]+)\]\]><\/status>/im
|
||||
)
|
||||
const matchGrade = body.match(
|
||||
/<grade><!\[CDATA\[([a-z]+)\]\]><\/grade>/im
|
||||
)
|
||||
|
||||
if (matchStatus === null) {
|
||||
badgeData.text[1] = 'inaccessible'
|
||||
sendBadge(format, badgeData)
|
||||
return
|
||||
} else if (matchStatus[1] !== 'finished') {
|
||||
badgeData.text[1] = 'pending'
|
||||
sendBadge(format, badgeData)
|
||||
return
|
||||
} else if (matchGrade === null) {
|
||||
badgeData.text[1] = 'invalid'
|
||||
sendBadge(format, badgeData)
|
||||
return
|
||||
}
|
||||
|
||||
if (matchGrade[1] === 'platinum') {
|
||||
badgeData.text[1] = 'platinum'
|
||||
badgeData.colorscheme = 'brightgreen'
|
||||
} else if (matchGrade[1] === 'gold') {
|
||||
badgeData.text[1] = 'gold'
|
||||
badgeData.colorscheme = 'yellow'
|
||||
} else if (matchGrade[1] === 'silver') {
|
||||
badgeData.text[1] = 'silver'
|
||||
badgeData.colorscheme = 'lightgrey'
|
||||
} else if (matchGrade[1] === 'bronze') {
|
||||
badgeData.text[1] = 'bronze'
|
||||
badgeData.colorscheme = 'orange'
|
||||
} else if (matchGrade[1] === 'none') {
|
||||
badgeData.text[1] = 'no medal'
|
||||
badgeData.colorscheme = 'red'
|
||||
} else {
|
||||
badgeData.text[1] = 'invalid'
|
||||
sendBadge(format, badgeData)
|
||||
return
|
||||
}
|
||||
|
||||
sendBadge(format, badgeData)
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
289
services/symfony/symfony-insight.service.js
Normal file
289
services/symfony/symfony-insight.service.js
Normal file
@@ -0,0 +1,289 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const BaseXmlService = require('../base-xml')
|
||||
const serverSecrets = require('../../lib/server-secrets')
|
||||
const { Inaccessible } = require('../errors')
|
||||
|
||||
const violationSchema = Joi.object({
|
||||
severity: Joi.equal('info', 'minor', 'major', 'critical').required(),
|
||||
}).required()
|
||||
|
||||
const schema = Joi.object({
|
||||
project: Joi.object({
|
||||
'last-analysis': Joi.object({
|
||||
status: Joi.equal(
|
||||
'ordered',
|
||||
'running',
|
||||
'measured',
|
||||
'analyzed',
|
||||
'finished'
|
||||
).required(),
|
||||
grade: Joi.equal('platinum', 'gold', 'silver', 'bronze', 'none'),
|
||||
violations: Joi.object({
|
||||
// RE: https://github.com/NaturalIntelligence/fast-xml-parser/issues/68
|
||||
// The BaseXmlService uses the fast-xml-parser which doesn't support forcing
|
||||
// the xml nodes to always be parsed as an array. Currently, if the response
|
||||
// only contains a single violation then it will be parsed as an object,
|
||||
// otherwise it will be parsed as an array.
|
||||
violation: Joi.array()
|
||||
.items(violationSchema)
|
||||
.single()
|
||||
.required(),
|
||||
}),
|
||||
}),
|
||||
}).required(),
|
||||
}).required()
|
||||
|
||||
const keywords = ['sensiolabs']
|
||||
|
||||
module.exports = class SymfonyInsight extends BaseXmlService {
|
||||
static render({
|
||||
metric,
|
||||
status,
|
||||
grade,
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
}) {
|
||||
if (status !== 'finished') {
|
||||
return {
|
||||
label: metric,
|
||||
message: 'pending',
|
||||
color: 'lightgrey',
|
||||
}
|
||||
}
|
||||
|
||||
if (metric === 'grade') {
|
||||
return this.renderGradeBadge({ grade })
|
||||
} else {
|
||||
return this.renderViolationsBadge({
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
static renderGradeBadge({ grade }) {
|
||||
let color,
|
||||
message = grade
|
||||
if (grade === 'platinum') {
|
||||
color = '#E5E4E2'
|
||||
} else if (grade === 'gold') {
|
||||
color = '#EBC760'
|
||||
} else if (grade === 'silver') {
|
||||
color = '#C0C0C0'
|
||||
} else if (grade === 'bronze') {
|
||||
color = '#C88F6A'
|
||||
} else {
|
||||
message = 'no medal'
|
||||
color = 'red'
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'grade',
|
||||
message,
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
static renderViolationsBadge({
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
}) {
|
||||
if (numViolations === 0) {
|
||||
return {
|
||||
label: 'violations',
|
||||
message: '0',
|
||||
color: 'brightgreen',
|
||||
}
|
||||
}
|
||||
|
||||
let color = 'yellowgreen'
|
||||
const violationSummary = []
|
||||
|
||||
if (numInfoViolations > 0) {
|
||||
violationSummary.push(`${numInfoViolations} info`)
|
||||
}
|
||||
if (numMinorViolations > 0) {
|
||||
violationSummary.unshift(`${numMinorViolations} minor`)
|
||||
color = 'yellow'
|
||||
}
|
||||
if (numMajorViolations > 0) {
|
||||
violationSummary.unshift(`${numMajorViolations} major`)
|
||||
color = 'orange'
|
||||
}
|
||||
if (numCriticalViolations > 0) {
|
||||
violationSummary.unshift(`${numCriticalViolations} critical`)
|
||||
color = 'red'
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'violations',
|
||||
message: violationSummary.join(', '),
|
||||
color,
|
||||
}
|
||||
}
|
||||
|
||||
static get defaultBadgeData() {
|
||||
return {
|
||||
label: 'symfony insight',
|
||||
}
|
||||
}
|
||||
|
||||
static get category() {
|
||||
return 'quality'
|
||||
}
|
||||
|
||||
static get route() {
|
||||
return {
|
||||
base: '',
|
||||
// The SymfonyInsight service was previously branded as SensioLabs, and
|
||||
// accordingly the badge path used to be /sensiolabs/i/projectUuid'.
|
||||
// This is used to provide backward compatibility for the old path as well as
|
||||
// supporting the new/current path.
|
||||
format: '(?:sensiolabs/i|symfony/i/(grade|violations))/([^/]+)',
|
||||
capture: ['metric', 'projectUuid'],
|
||||
}
|
||||
}
|
||||
|
||||
static get examples() {
|
||||
return [
|
||||
{
|
||||
title: 'SymfonyInsight Grade',
|
||||
pattern: 'symfony/i/grade/:projectUuid',
|
||||
namedParams: {
|
||||
projectUuid: '45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
|
||||
},
|
||||
staticPreview: this.renderGradeBadge({
|
||||
grade: 'bronze',
|
||||
}),
|
||||
keywords,
|
||||
},
|
||||
{
|
||||
title: 'SymfonyInsight Violations',
|
||||
pattern: 'symfony/i/violations/:projectUuid',
|
||||
namedParams: {
|
||||
projectUuid: '45afb680-d4e6-4e66-93ea-bcfa79eb8a87',
|
||||
},
|
||||
staticPreview: this.renderViolationsBadge({
|
||||
numViolations: 0,
|
||||
}),
|
||||
keywords,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
async fetch({ projectUuid }) {
|
||||
const url = `https://insight.symfony.com/api/projects/${projectUuid}`
|
||||
const options = {
|
||||
headers: {
|
||||
Accept: 'application/vnd.com.sensiolabs.insight+xml',
|
||||
},
|
||||
}
|
||||
|
||||
if (
|
||||
!serverSecrets.sl_insight_userUuid ||
|
||||
!serverSecrets.sl_insight_apiToken
|
||||
) {
|
||||
throw new Inaccessible({
|
||||
prettyMessage: 'required API tokens not found in config',
|
||||
})
|
||||
}
|
||||
|
||||
options.auth = {
|
||||
user: serverSecrets.sl_insight_userUuid,
|
||||
pass: serverSecrets.sl_insight_apiToken,
|
||||
}
|
||||
|
||||
return this._requestXml({
|
||||
url,
|
||||
options,
|
||||
schema,
|
||||
errorMessages: {
|
||||
401: 'not authorized to access project',
|
||||
404: 'project not found',
|
||||
},
|
||||
parserOptions: {
|
||||
attributeNamePrefix: '',
|
||||
ignoreAttributes: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
transform({ data }) {
|
||||
const lastAnalysis = data.project['last-analysis']
|
||||
let numViolations = 0
|
||||
let numCriticalViolations = 0
|
||||
let numMajorViolations = 0
|
||||
let numMinorViolations = 0
|
||||
let numInfoViolations = 0
|
||||
|
||||
const violationContainer = lastAnalysis.violations
|
||||
if (violationContainer && violationContainer.violation) {
|
||||
let violations = []
|
||||
// See above note on schema RE: https://github.com/NaturalIntelligence/fast-xml-parser/issues/68
|
||||
// This covers the scenario of multiple violations which are parsed as an array and single
|
||||
// violations which is parsed as a single object.
|
||||
if (Array.isArray(violationContainer.violation)) {
|
||||
violations = violationContainer.violation
|
||||
} else {
|
||||
violations.push(violationContainer.violation)
|
||||
}
|
||||
numViolations = violations.length
|
||||
violations.forEach(violation => {
|
||||
if (violation.severity === 'critical') {
|
||||
numCriticalViolations++
|
||||
} else if (violation.severity === 'major') {
|
||||
numMajorViolations++
|
||||
} else if (violation.severity === 'minor') {
|
||||
numMinorViolations++
|
||||
} else {
|
||||
numInfoViolations++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
status: lastAnalysis.status,
|
||||
grade: lastAnalysis.grade,
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
}
|
||||
}
|
||||
|
||||
async handle({ metric = 'grade', projectUuid }) {
|
||||
const data = await this.fetch({ projectUuid })
|
||||
const {
|
||||
status,
|
||||
grade,
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
} = this.transform({ data })
|
||||
|
||||
return this.constructor.render({
|
||||
metric,
|
||||
status,
|
||||
grade,
|
||||
numViolations,
|
||||
numCriticalViolations,
|
||||
numMajorViolations,
|
||||
numMinorViolations,
|
||||
numInfoViolations,
|
||||
})
|
||||
}
|
||||
}
|
||||
318
services/symfony/symfony-insight.tester.js
Normal file
318
services/symfony/symfony-insight.tester.js
Normal file
@@ -0,0 +1,318 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { colorScheme } = require('../test-helpers')
|
||||
const t = (module.exports = require('../create-service-tester')())
|
||||
const { withRegex } = require('../test-validators')
|
||||
|
||||
const {
|
||||
runningMockResponse,
|
||||
platinumMockResponse,
|
||||
goldMockResponse,
|
||||
silverMockResponse,
|
||||
bronzeMockResponse,
|
||||
noMedalMockResponse,
|
||||
mockSymfonyUser,
|
||||
mockSymfonyToken,
|
||||
mockSymfonyInsightCreds,
|
||||
setSymfonyInsightCredsToFalsy,
|
||||
restore,
|
||||
realTokenExists,
|
||||
prepLiveTest,
|
||||
criticalViolation,
|
||||
majorViolation,
|
||||
minorViolation,
|
||||
infoViolation,
|
||||
multipleViolations,
|
||||
} = require('./symfony-test-helpers')
|
||||
|
||||
const sampleProjectUuid = '45afb680-d4e6-4e66-93ea-bcfa79eb8a87'
|
||||
|
||||
function create(title, { withMockCreds = true } = { withMockCreds: true }) {
|
||||
const result = t.create(title)
|
||||
if (withMockCreds) {
|
||||
result.before(mockSymfonyInsightCreds)
|
||||
result.finally(restore)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
create('live: valid project grade', { withMockCreds: false })
|
||||
.before(prepLiveTest)
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json`)
|
||||
.timeout(15000)
|
||||
.interceptIf(!realTokenExists, nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, platinumMockResponse)
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'grade',
|
||||
value: Joi.equal(
|
||||
'platinum',
|
||||
'gold',
|
||||
'silver',
|
||||
'bronze',
|
||||
'no medal'
|
||||
).required(),
|
||||
})
|
||||
)
|
||||
|
||||
create('live: valid project violations', { withMockCreds: false })
|
||||
.before(prepLiveTest)
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json`)
|
||||
.timeout(15000)
|
||||
.interceptIf(!realTokenExists, nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, multipleViolations)
|
||||
)
|
||||
.expectJSONTypes(
|
||||
Joi.object().keys({
|
||||
name: 'violations',
|
||||
value: withRegex(
|
||||
/\d* critical|\d* critical, \d* major|\d* critical, \d* major, \d* minor|\d* critical, \d* major, \d* minor, \d* info|\d* critical, \d* minor|\d* critical, \d* info|\d* major|\d* major, \d* minor|\d* major, \d* minor, \d* info|\d* major, \d* info|\d* minor|\d* minor, \d* info/
|
||||
),
|
||||
})
|
||||
)
|
||||
|
||||
create('live: nonexistent project', { withMockCreds: false })
|
||||
.before(prepLiveTest)
|
||||
.get('/symfony/i/grade/45afb680-d4e6-4e66-93ea-bcfa79eb8a88.json')
|
||||
.interceptIf(!realTokenExists, nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get('/45afb680-d4e6-4e66-93ea-bcfa79eb8a88')
|
||||
.reply(404)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'symfony insight',
|
||||
value: 'project not found',
|
||||
})
|
||||
|
||||
create('404 project not found grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(404)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'symfony insight',
|
||||
value: 'project not found',
|
||||
})
|
||||
|
||||
create('401 not authorized grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(401)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'symfony insight',
|
||||
value: 'not authorized to access project',
|
||||
})
|
||||
|
||||
create('pending project grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, runningMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'pending',
|
||||
colorB: colorScheme.lightgrey,
|
||||
})
|
||||
|
||||
create('platinum grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, platinumMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'platinum',
|
||||
colorB: '#E5E4E2',
|
||||
})
|
||||
|
||||
create('gold grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, goldMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'gold',
|
||||
colorB: '#EBC760',
|
||||
})
|
||||
|
||||
create('silver grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, silverMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'silver',
|
||||
colorB: '#C0C0C0',
|
||||
})
|
||||
|
||||
create('bronze grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, bronzeMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'bronze',
|
||||
colorB: '#C88F6A',
|
||||
})
|
||||
|
||||
create('no medal grade')
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, noMedalMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'no medal',
|
||||
colorB: colorScheme.red,
|
||||
})
|
||||
|
||||
create('zero violations')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, goldMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '0',
|
||||
colorB: colorScheme.brightgreen,
|
||||
})
|
||||
|
||||
create('critical violations')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, criticalViolation)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '1 critical',
|
||||
colorB: colorScheme.red,
|
||||
})
|
||||
|
||||
create('major violations')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, majorViolation)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '1 major',
|
||||
colorB: colorScheme.orange,
|
||||
})
|
||||
|
||||
create('minor violations')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.basicAuth({
|
||||
user: mockSymfonyUser,
|
||||
pass: mockSymfonyToken,
|
||||
})
|
||||
.reply(200, minorViolation)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '1 minor',
|
||||
colorB: colorScheme.yellow,
|
||||
})
|
||||
|
||||
create('info violations')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.basicAuth({
|
||||
user: mockSymfonyUser,
|
||||
pass: mockSymfonyToken,
|
||||
})
|
||||
.reply(200, infoViolation)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '1 info',
|
||||
colorB: colorScheme.yellowgreen,
|
||||
})
|
||||
|
||||
create('multiple violations grade')
|
||||
.get(`/symfony/i/violations/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.basicAuth({
|
||||
user: mockSymfonyUser,
|
||||
pass: mockSymfonyToken,
|
||||
})
|
||||
.reply(200, multipleViolations)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'violations',
|
||||
value: '1 critical, 1 info',
|
||||
colorB: colorScheme.red,
|
||||
})
|
||||
|
||||
create('auth missing', { withMockCreds: false })
|
||||
.before(setSymfonyInsightCredsToFalsy)
|
||||
.get(`/symfony/i/grade/${sampleProjectUuid}.json`)
|
||||
.expectJSON({
|
||||
name: 'symfony insight',
|
||||
value: 'required API tokens not found in config',
|
||||
})
|
||||
|
||||
// These tests ensure that the legacy badge path (/sensiolabs/i/projectUuid) still works
|
||||
create('legacy path: pending project grade')
|
||||
.get(`/sensiolabs/i/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, runningMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'pending',
|
||||
colorB: colorScheme.lightgrey,
|
||||
})
|
||||
|
||||
create('legacy path: platinum grade')
|
||||
.get(`/sensiolabs/i/${sampleProjectUuid}.json?style=_shields_test`)
|
||||
.intercept(nock =>
|
||||
nock('https://insight.symfony.com/api/projects')
|
||||
.get(`/${sampleProjectUuid}`)
|
||||
.reply(200, platinumMockResponse)
|
||||
)
|
||||
.expectJSON({
|
||||
name: 'grade',
|
||||
value: 'platinum',
|
||||
colorB: '#E5E4E2',
|
||||
})
|
||||
134
services/symfony/symfony-test-helpers.js
Normal file
134
services/symfony/symfony-test-helpers.js
Normal file
@@ -0,0 +1,134 @@
|
||||
'use strict'
|
||||
|
||||
const sinon = require('sinon')
|
||||
const serverSecrets = require('../../lib/server-secrets')
|
||||
|
||||
function createMockResponse({ status = 'finished', grade, violations }) {
|
||||
let response = `
|
||||
<project>
|
||||
<last-analysis>
|
||||
<status><![CDATA[${status}]]></status>
|
||||
${grade ? `<grade><![CDATA[${grade}]]></grade>` : ''}`
|
||||
if (violations) {
|
||||
response = `${response}<violations>`
|
||||
violations.forEach(v => {
|
||||
response = `${response}<violation severity="${v.severity}"></violation>`
|
||||
})
|
||||
response = `${response}</violations>`
|
||||
}
|
||||
return `${response}</last-analysis></project>`
|
||||
}
|
||||
|
||||
const runningMockResponse = createMockResponse({
|
||||
status: 'running',
|
||||
})
|
||||
const platinumMockResponse = createMockResponse({
|
||||
grade: 'platinum',
|
||||
})
|
||||
const goldMockResponse = createMockResponse({
|
||||
grade: 'gold',
|
||||
})
|
||||
const silverMockResponse = createMockResponse({
|
||||
grade: 'silver',
|
||||
})
|
||||
const bronzeMockResponse = createMockResponse({
|
||||
grade: 'bronze',
|
||||
})
|
||||
const noMedalMockResponse = createMockResponse({
|
||||
grade: 'none',
|
||||
})
|
||||
const criticalViolation = createMockResponse({
|
||||
violations: [
|
||||
{
|
||||
severity: 'critical',
|
||||
},
|
||||
],
|
||||
})
|
||||
const majorViolation = createMockResponse({
|
||||
violations: [
|
||||
{
|
||||
severity: 'major',
|
||||
},
|
||||
],
|
||||
})
|
||||
const minorViolation = createMockResponse({
|
||||
violations: [
|
||||
{
|
||||
severity: 'minor',
|
||||
},
|
||||
],
|
||||
})
|
||||
const infoViolation = createMockResponse({
|
||||
violations: [
|
||||
{
|
||||
severity: 'info',
|
||||
},
|
||||
],
|
||||
})
|
||||
const multipleViolations = createMockResponse({
|
||||
violations: [
|
||||
{
|
||||
severity: 'info',
|
||||
},
|
||||
{
|
||||
severity: 'critical',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const mockSymfonyUser = 'admin'
|
||||
const mockSymfonyToken = 'password'
|
||||
const originalUuid = serverSecrets.sl_insight_userUuid
|
||||
const originalApiToken = serverSecrets.sl_insight_apiToken
|
||||
|
||||
function setSymfonyInsightCredsToFalsy() {
|
||||
serverSecrets['sl_insight_userUuid'] = undefined
|
||||
serverSecrets['sl_insight_apiToken'] = undefined
|
||||
}
|
||||
|
||||
function mockSymfonyInsightCreds() {
|
||||
// ensure that the fields exists before attempting to stub
|
||||
setSymfonyInsightCredsToFalsy()
|
||||
sinon.stub(serverSecrets, 'sl_insight_userUuid').value(mockSymfonyUser)
|
||||
sinon.stub(serverSecrets, 'sl_insight_apiToken').value(mockSymfonyToken)
|
||||
}
|
||||
|
||||
function restore() {
|
||||
sinon.restore()
|
||||
serverSecrets['sl_insight_userUuid'] = originalUuid
|
||||
serverSecrets['sl_insight_apiToken'] = originalApiToken
|
||||
}
|
||||
|
||||
function prepLiveTest() {
|
||||
// Since the service implementation will throw an error if the creds
|
||||
// are missing, we need to ensure that creds are available for each test.
|
||||
// In the case of the live tests we want to use the "real" creds if they
|
||||
// exist otherwise we need to use the same stubbed creds as all the mocked tests.
|
||||
if (!originalUuid) {
|
||||
console.warn(
|
||||
'No token provided, this test will mock Symfony Insight API responses.'
|
||||
)
|
||||
mockSymfonyInsightCreds()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runningMockResponse,
|
||||
platinumMockResponse,
|
||||
goldMockResponse,
|
||||
silverMockResponse,
|
||||
bronzeMockResponse,
|
||||
noMedalMockResponse,
|
||||
mockSymfonyUser,
|
||||
mockSymfonyToken,
|
||||
mockSymfonyInsightCreds,
|
||||
setSymfonyInsightCredsToFalsy,
|
||||
restore,
|
||||
realTokenExists: originalUuid,
|
||||
prepLiveTest,
|
||||
criticalViolation,
|
||||
majorViolation,
|
||||
minorViolation,
|
||||
infoViolation,
|
||||
multipleViolations,
|
||||
}
|
||||
Reference in New Issue
Block a user