Skip to content

Commit 59a0451

Browse files
authored
Merge pull request North-Seattle-College#129 from gillilands19/SG-artillery-perf
[Task] Create and Deploy Serverless Artillery test for /Users/{UserID}/Tasks/{TaskID}
2 parents 4b0c4e0 + c678ab1 commit 59a0451

18 files changed

+1566
-16761
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,6 @@ DS_Store
117117

118118
# ignore .vscode
119119
.vscode/
120+
121+
package-lock.json
122+

package-lock.json

-3
This file was deleted.

test/artillery/.slsart

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
version: 0.0.1

test/artillery/alert.js

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
const aws = require('aws-sdk') // eslint-disable-line import/no-extraneous-dependencies
2+
3+
const sns = new aws.SNS()
4+
5+
const alert = {
6+
briefAnalysis: (analysis) => {
7+
const latencies = []
8+
analysis.reports.forEach((report, i) => {
9+
const current = report
10+
latencies[i] = current.latencies
11+
delete current.latencies
12+
})
13+
const ret = JSON.stringify(analysis, null, 2)
14+
analysis.reports.forEach((report, i) => {
15+
const current = report
16+
current.latencies = latencies[i]
17+
})
18+
return ret
19+
},
20+
send: (script, analysis) => {
21+
if (!process.env.TOPIC_ARN) {
22+
console.error([
23+
'#########################################################',
24+
'## ! Required Configuration Missing ! ##',
25+
'## in order to send the alert, an environment variable ##',
26+
'## TOPIC_ARN must be available. An alert was supposed ##',
27+
'## to be sent but one cannot be sent! ##',
28+
'#########################################################',
29+
].join('\n'))
30+
return Promise.resolve()
31+
} else {
32+
const subject = `Alert: ${analysis.errorMessage}`
33+
const message = `Alert:
34+
${analysis.errorMessage}
35+
36+
Logs:
37+
Full analysis:
38+
${alert.briefAnalysis(analysis)}
39+
`
40+
const params = {
41+
Subject: subject,
42+
Message: message,
43+
TopicArn: process.env.TOPIC_ARN,
44+
}
45+
return sns.publish(params).promise()
46+
}
47+
},
48+
}
49+
50+
module.exports = alert

test/artillery/analysis.js

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
const modes = require('./modes.js')
2+
const sampling = require('./sampling.js')
3+
4+
const analysis = {
5+
getErrorBudget: (script, defaultErrorBudget) => {
6+
if (script && script.sampling && script.sampling.errorBudget !== undefined) {
7+
return script.sampling.errorBudget
8+
} else if (defaultErrorBudget !== undefined) {
9+
return defaultErrorBudget
10+
} else {
11+
return sampling.defaults.sampling.errorBudget
12+
}
13+
},
14+
15+
/**
16+
* Analyze a set of sampling reports, each of which is the result of a sample battery's execution
17+
* results.
18+
* @param script Artillery script used
19+
* @param report The collection of reports to analyze
20+
* @param defaultErrorBudget Maximum number of errors allowed
21+
*/
22+
analyzeSamples: (script, report, defaultErrorBudget) => {
23+
let reports
24+
25+
if (report.reports) {
26+
reports = report.reports // eslint-disable-line prefer-destructuring
27+
} else {
28+
reports = [report]
29+
}
30+
31+
const totals = {
32+
scenariosCreated: 0,
33+
scenariosCompleted: 0,
34+
requestsCompleted: 0,
35+
codes: {},
36+
errors: {},
37+
}
38+
39+
const mergeReportPropertyCounts = property =>
40+
reports.forEach(test =>
41+
Object.keys(test[property]).forEach((key) => {
42+
const countForTest = test[property][key]
43+
const isNewKey = totals[property][key] === undefined
44+
45+
if (isNewKey) {
46+
totals[property][key] = countForTest
47+
} else {
48+
totals[property][key] += countForTest
49+
}
50+
}))
51+
52+
mergeReportPropertyCounts('codes')
53+
mergeReportPropertyCounts('errors')
54+
55+
const accumulateReportTotals = property =>
56+
reports.forEach((test) => { totals[property] += test[property] })
57+
58+
accumulateReportTotals('scenariosCreated')
59+
accumulateReportTotals('scenariosCompleted')
60+
accumulateReportTotals('requestsCompleted')
61+
62+
const totalErrors = Object.keys(totals.errors).reduce((total, message) => total + totals.errors[message], 0)
63+
64+
const errorBudget = analysis.getErrorBudget(script, defaultErrorBudget)
65+
66+
const finalReport = {
67+
errors: totalErrors,
68+
reports,
69+
totals,
70+
}
71+
72+
if (totalErrors > errorBudget) {
73+
finalReport.errorMessage = `${
74+
modes.getModeForDisplay(script)
75+
} failure: scenarios run: ${
76+
totals.scenariosCreated
77+
}, total errors: ${
78+
totalErrors
79+
}, error budget: ${
80+
errorBudget
81+
}`
82+
}
83+
84+
return finalReport
85+
},
86+
87+
/**
88+
* Analyze a set of reports, each of which is the result of an acceptance test's execution
89+
* @param timeNow Origin time of the artillery test
90+
* @param script Artillery script used
91+
* @param settings Platform settings used
92+
* @param results Results of the tests
93+
*
94+
* @returns Object
95+
*/
96+
analyzeAcceptance: (timeNow, script, settings, results) =>
97+
analysis.analyzeSamples(script, results, sampling.defaults.acceptance.errorBudget),
98+
99+
/**
100+
* Analyze a set of reports, each of which is the result of an monitoring test's execution
101+
* @param timeNow Origin time of the artillery test
102+
* @param script Artillery script used
103+
* @param settings Platform settings used
104+
* @param results Results of the tests
105+
*
106+
* @returns Object
107+
*/
108+
analyzeMonitoring: (timeNow, script, settings, results) =>
109+
analysis.analyzeSamples(script, results, sampling.defaults.monitoring.errorBudget),
110+
111+
/**
112+
* Analyze the performance results. If there is one payload, then it is a report. Otherwise, it is a set of payloads.
113+
* @param timeNow Origin time of the artillery test
114+
* @param script Artillery script used
115+
* @param settings Platform settings used
116+
* @param results Results of the tests
117+
*
118+
* @returns Object
119+
*/
120+
analyzePerformance: (timeNow, script, settings, results) => {
121+
if (results && results.length === 1) {
122+
return results[0] // return the report
123+
} else if (results) {
124+
return results
125+
} else { // eslint-disable-next-line no-underscore-dangle
126+
return { message: `load test from ${script._genesis} successfully completed from ${timeNow} @ ${Date.now()}` }
127+
}
128+
},
129+
}
130+
131+
module.exports = analysis
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const sampling = require('./sampling.js')
2+
const planning = require('./planning.js')
3+
const analysis = require('./analysis.js')
4+
5+
const artilleryAcceptance = artilleryTask => ({
6+
execute: (timeNow, script, settings) => {
7+
const acceptanceScript = sampling.applyAcceptanceSamplingToScript(script, settings)
8+
9+
const plans = planning.planSamples(timeNow, acceptanceScript, settings)
10+
11+
return artilleryTask
12+
.executeAll(acceptanceScript, settings, plans, timeNow)
13+
.then(results => analysis.analyzeAcceptance(timeNow, acceptanceScript, settings, results))
14+
},
15+
})
16+
17+
module.exports = artilleryAcceptance
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
const sampling = require('./sampling.js')
2+
const planning = require('./planning.js')
3+
const analysis = require('./analysis.js')
4+
const alert = require('./alert.js')
5+
6+
const artilleryMonitoring = artillery => ({
7+
execute: (timeNow, script, settings) => {
8+
const monitorScript = sampling.applyMonitoringSamplingToScript(script, settings)
9+
10+
const plans = planning.planSamples(timeNow, monitorScript, settings)
11+
12+
return artillery.executeAll(monitorScript, settings, plans, timeNow)
13+
.then(results => analysis.analyzeMonitoring(timeNow, script, settings, results))
14+
.then((monitoringResults) => {
15+
if (monitoringResults.errorMessage) {
16+
return alert.send(script, monitoringResults)
17+
.then(() => monitoringResults)
18+
}
19+
20+
return monitoringResults
21+
})
22+
},
23+
})
24+
25+
module.exports = artilleryMonitoring
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const planning = require('./planning.js')
2+
const analysis = require('./analysis.js')
3+
4+
const artilleryPerformance = artilleryTask => ({
5+
execute: (timeNow, script, settings) => {
6+
const plans = planning.planPerformance(timeNow, script, settings)
7+
8+
return artilleryTask.executeAll(script, settings, plans, timeNow)
9+
.then(results => analysis.analyzePerformance(timeNow, script, settings, results))
10+
},
11+
})
12+
13+
module.exports = artilleryPerformance

0 commit comments

Comments
 (0)