testaro 69.5.0 → 69.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -11
- package/netWatch.js +85 -86
- package/package.json +1 -1
- package/procs/doActs.js +9 -7
- package/run.js +2 -0
- package/tests/nuVal.js +2 -2
package/README.md
CHANGED
|
@@ -132,17 +132,15 @@ REPORTDIR=../testing/reports
|
|
|
132
132
|
# Name of this Testaro instance when it listens for jobs and sends reports to requesting hosts.
|
|
133
133
|
AGENT=agentabc
|
|
134
134
|
#----------------------------
|
|
135
|
-
# When Testaro polls network
|
|
136
|
-
# URL
|
|
137
|
-
|
|
138
|
-
# URL
|
|
139
|
-
|
|
140
|
-
# URL
|
|
141
|
-
|
|
142
|
-
# Password to give to host
|
|
143
|
-
|
|
144
|
-
# Which network hosts to poll for jobs (comma-separated list of indices).
|
|
145
|
-
NETWATCH_URLS=0
|
|
135
|
+
# When Testaro polls a network host to ask for new jobs, data on the host.
|
|
136
|
+
# URL to poll.
|
|
137
|
+
NETWATCH_JOB=http://localhost:3000/api/assignJob/agentabc
|
|
138
|
+
# URL to which to send progress reports during jobs.
|
|
139
|
+
NETWATCH_OBSERVE=http://localhost:3000/api/granular/agentabc
|
|
140
|
+
# URL to which to send completed job reports.
|
|
141
|
+
NETWATCH_REPORT=http://localhost:3000/api/takeReport/agentabc
|
|
142
|
+
# Password to give to the host to authenticate this instance.
|
|
143
|
+
NETWATCH_AUTH=abcxyz
|
|
146
144
|
```
|
|
147
145
|
|
|
148
146
|
## Jobs
|
package/netWatch.js
CHANGED
|
@@ -29,10 +29,11 @@ const {nowString} = require('./procs/dateTime');
|
|
|
29
29
|
|
|
30
30
|
// CONSTANTS
|
|
31
31
|
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
32
|
+
const jobURL = new URL(process.env.NETWATCH_URL_JOB);
|
|
33
|
+
const jobHost = jobURL.host;
|
|
34
|
+
const reportURL = new URL(process.env.NETWATCH_URL_REPORT);
|
|
35
|
+
const reportHost = reportURL.host;
|
|
36
|
+
const agentPW = process.env.NETWATCH_URL_AUTH;
|
|
36
37
|
|
|
37
38
|
// FUNCTIONS
|
|
38
39
|
|
|
@@ -44,39 +45,22 @@ const wait = ms => {
|
|
|
44
45
|
}, ms);
|
|
45
46
|
});
|
|
46
47
|
};
|
|
47
|
-
//
|
|
48
|
-
const
|
|
49
|
-
response.setHeader('
|
|
48
|
+
// Ends a response with an object in JSON format.
|
|
49
|
+
const respondWithObject = (object, response) => {
|
|
50
|
+
response.setHeader('content-type', 'application/json; charset=utf-8');
|
|
50
51
|
response.end(JSON.stringify(object));
|
|
51
52
|
};
|
|
52
|
-
// Removes the query if any, or otherwise the final segment, from a URL.
|
|
53
|
-
const getURLBase = url => url.replace(/[?/][^?/.]+$/, '');
|
|
54
53
|
/*
|
|
55
54
|
Requests a network job and, when found, performs and reports it.
|
|
56
55
|
Arguments:
|
|
57
56
|
0. whether to continue watching after a job is run.
|
|
58
|
-
1: interval in seconds from a
|
|
57
|
+
1: interval in seconds from a no-job check to the next check.
|
|
59
58
|
2. whether to ignore unknown-certificate errors from watched servers.
|
|
60
59
|
*/
|
|
61
60
|
exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) => {
|
|
62
|
-
// If the job and report URLs exist and are
|
|
63
|
-
if (
|
|
64
|
-
jobURLs
|
|
65
|
-
&& jobURLs.length
|
|
66
|
-
&& reportURLs
|
|
67
|
-
&& reportURLs.length === jobURLs.length
|
|
68
|
-
&& jobURLs.every((jobURL, index) => {
|
|
69
|
-
const allDefined = [jobURL, reportURLs[index]].every(url => url);
|
|
70
|
-
const allSchemed = allDefined
|
|
71
|
-
&& [jobURL, reportURLs[index]]
|
|
72
|
-
.every(url => ['http://', 'https://'].some(prefix => url.startsWith(prefix)));
|
|
73
|
-
return allSchemed;
|
|
74
|
-
})
|
|
75
|
-
) {
|
|
61
|
+
// If the job and report URLs exist and are valid:
|
|
62
|
+
if (jobURL && reportURL) {
|
|
76
63
|
// Configure the watch.
|
|
77
|
-
const urlCount = jobURLs.length;
|
|
78
|
-
let cycleIndex = -1;
|
|
79
|
-
let urlIndex = -1;
|
|
80
64
|
let noJobYet = true;
|
|
81
65
|
let abort = false;
|
|
82
66
|
const certInfo = `Certificate-${isCertTolerant ? '' : 'in'}tolerant`;
|
|
@@ -87,122 +71,123 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
87
71
|
);
|
|
88
72
|
// As long as watching is to continue:
|
|
89
73
|
while ((isForever || noJobYet) && ! abort) {
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
// Wait for the specified interval.
|
|
93
|
-
await wait(1000 * intervalInSeconds);
|
|
94
|
-
// Log the start of a cycle.
|
|
95
|
-
console.log('--');
|
|
96
|
-
}
|
|
97
|
-
// Otherwise, i.e. if the cycle is incomplete:
|
|
98
|
-
else {
|
|
99
|
-
// Wait briefly.
|
|
100
|
-
await wait(1000);
|
|
101
|
-
}
|
|
74
|
+
// Log the start of a check.
|
|
75
|
+
console.log('--');
|
|
102
76
|
// Configure the next check.
|
|
103
|
-
|
|
104
|
-
urlIndex = ++urlIndex % urlCount;
|
|
105
|
-
const jobURL = jobURLs[urlIndex];
|
|
106
|
-
const publicURL = auths[urlIndex] ? jobURL : getURLBase(jobURL);
|
|
107
|
-
const logStart = `Requested job from ${publicURL} and got `;
|
|
77
|
+
const logStart = `Requested job from ${jobHost} and got `;
|
|
108
78
|
// Perform it.
|
|
109
79
|
await new Promise(resolve => {
|
|
110
80
|
try {
|
|
111
|
-
const client = jobURL.
|
|
81
|
+
const client = jobURL.protocol === 'https:' ? httpsClient : httpClient;
|
|
112
82
|
// Request a job.
|
|
113
|
-
const requestOptions =
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
83
|
+
const requestOptions = {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
host: jobHost,
|
|
87
|
+
'content-type': 'application/json; charset=utf-8'
|
|
88
|
+
}
|
|
89
|
+
};
|
|
117
90
|
client.request(jobURL, requestOptions, response => {
|
|
91
|
+
// Initialize a collection of data from the response.
|
|
118
92
|
const chunks = [];
|
|
119
93
|
response
|
|
120
94
|
// If the response throws an error:
|
|
121
95
|
.on('error', async error => {
|
|
122
96
|
// Report it.
|
|
123
97
|
console.log(`${logStart}error message ${error.message}`);
|
|
98
|
+
// Stop checking.
|
|
99
|
+
abort = true;
|
|
124
100
|
resolve(true);
|
|
125
101
|
})
|
|
102
|
+
// If the response delivers data:
|
|
126
103
|
.on('data', chunk => {
|
|
104
|
+
// Add them to the collection.
|
|
127
105
|
chunks.push(chunk);
|
|
128
106
|
})
|
|
129
|
-
// When the response
|
|
107
|
+
// When the response is completed:
|
|
130
108
|
.on('end', async () => {
|
|
131
109
|
const content = chunks.join('');
|
|
132
|
-
// It should be JSON. If it is:
|
|
133
110
|
try {
|
|
111
|
+
// Parse it as a JSON job.
|
|
134
112
|
let contentObj = JSON.parse(content);
|
|
135
113
|
const {id, sources} = contentObj;
|
|
136
|
-
// If it is
|
|
114
|
+
// If it is a no-job message:
|
|
137
115
|
if (! Object.keys(contentObj).length) {
|
|
138
116
|
// Report this.
|
|
139
|
-
console.log(`${logStart}no job to do`);
|
|
117
|
+
console.log(`${logStart}no job to do; waiting ${intervalInSeconds} sec before next check`);
|
|
118
|
+
// Wait for the specified interval.
|
|
119
|
+
await wait(1000 * intervalInSeconds);
|
|
140
120
|
resolve(true);
|
|
141
121
|
}
|
|
142
|
-
// Otherwise, if
|
|
122
|
+
// Otherwise, if a job was received:
|
|
143
123
|
else if (id) {
|
|
144
124
|
// Check it for validity.
|
|
145
|
-
const
|
|
125
|
+
const jobValidity = isValidJob(contentObj);
|
|
146
126
|
// If it is invalid:
|
|
147
|
-
if (!
|
|
127
|
+
if (! jobValidity.isValid) {
|
|
148
128
|
// Report this to the server.
|
|
149
|
-
|
|
129
|
+
respondWithObject({
|
|
150
130
|
message: `invalidJob`,
|
|
151
|
-
error:
|
|
131
|
+
error: jobValidity.error
|
|
152
132
|
}, response);
|
|
153
|
-
console.log(`${logStart}invalid job (${
|
|
133
|
+
console.log(`${logStart}invalid job (${jobValidity.error})`);
|
|
154
134
|
resolve(true);
|
|
155
135
|
}
|
|
156
136
|
// Otherwise, i.e. if it is valid:
|
|
157
137
|
else {
|
|
158
|
-
// Restart the cycle.
|
|
159
|
-
cycleIndex = -1;
|
|
160
138
|
// Prevent further watching, if unwanted.
|
|
161
139
|
noJobYet = false;
|
|
162
140
|
// Add the agent and the server ID to the job.
|
|
163
141
|
sources.agent = process.env.AGENT || '';
|
|
164
|
-
sources.serverID = urlIndex;
|
|
165
142
|
// Perform the job and create a report.
|
|
166
|
-
console.log(`${logStart}job ${id}
|
|
143
|
+
console.log(`${logStart}job ${id} (${nowString()})`);
|
|
167
144
|
try {
|
|
168
145
|
const report = await doJob(contentObj);
|
|
169
|
-
const responseObj =
|
|
170
|
-
agentPW
|
|
146
|
+
const responseObj = {
|
|
147
|
+
agentPW,
|
|
171
148
|
report
|
|
172
|
-
}
|
|
149
|
+
};
|
|
173
150
|
let responseJSON = JSON.stringify(responseObj, null, 2);
|
|
174
151
|
console.log(`Job ${id} finished (${nowString()})`);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
152
|
+
const reportLogStart = `Submitted report ${id} to ${reportURL} and got `;
|
|
153
|
+
const requestOptions = {
|
|
154
|
+
method: 'POST',
|
|
155
|
+
headers: {
|
|
156
|
+
host: reportHost,
|
|
157
|
+
'content-type': 'application/json; charset=utf-8'
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
// Submit the report.
|
|
161
|
+
const client = reportURL.protocol === 'https:' ? httpsClient : httpClient;
|
|
162
|
+
client.request(reportURL, requestOptions, repResponse => {
|
|
163
|
+
// Initialize a collection of data from the response.
|
|
183
164
|
const chunks = [];
|
|
184
165
|
repResponse
|
|
185
166
|
// If the response to the report threw an error:
|
|
186
167
|
.on('error', async error => {
|
|
187
168
|
// Report this.
|
|
188
169
|
console.log(`${reportLogStart}error message ${error.message}\n`);
|
|
170
|
+
// Stop checking.
|
|
171
|
+
abort = true;
|
|
189
172
|
resolve(true);
|
|
190
173
|
})
|
|
174
|
+
// If the response delivers data:
|
|
191
175
|
.on('data', chunk => {
|
|
176
|
+
// Add them to the collection.
|
|
192
177
|
chunks.push(chunk);
|
|
193
178
|
})
|
|
194
|
-
// When the response to the report
|
|
179
|
+
// When the response to the report is completed:
|
|
195
180
|
.on('end', async () => {
|
|
196
181
|
const content = chunks.join('');
|
|
197
|
-
// It should be JSON. If it is:
|
|
198
182
|
try {
|
|
183
|
+
// Parse it as a JSON message.
|
|
199
184
|
const ackObj = JSON.parse(content);
|
|
200
185
|
// Report it.
|
|
201
186
|
console.log(
|
|
202
187
|
`${reportLogStart}response message: ${JSON.stringify(ackObj, null, 2)}\n`
|
|
203
188
|
);
|
|
204
189
|
}
|
|
205
|
-
//
|
|
190
|
+
// If it is not JSON:
|
|
206
191
|
catch(error) {
|
|
207
192
|
// Report this.
|
|
208
193
|
console.log(
|
|
@@ -223,6 +208,8 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
223
208
|
console.log(
|
|
224
209
|
`ERROR ${error.code} in report submission: ${reportLogStart}error message ${error.message}\n`
|
|
225
210
|
);
|
|
211
|
+
// Stop checking.
|
|
212
|
+
abort = true;
|
|
226
213
|
resolve(true);
|
|
227
214
|
})
|
|
228
215
|
// Finish submitting the report.
|
|
@@ -230,6 +217,8 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
230
217
|
}
|
|
231
218
|
catch(error) {
|
|
232
219
|
console.log(`ERROR performing job ${id} (${error.message})`);
|
|
220
|
+
// Stop checking.
|
|
221
|
+
abort = true;
|
|
233
222
|
resolve(true);
|
|
234
223
|
}
|
|
235
224
|
}
|
|
@@ -245,6 +234,8 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
245
234
|
catch(error) {
|
|
246
235
|
// Report this.
|
|
247
236
|
console.log(`ERROR: ${logStart}status ${response.statusCode}, error message ${error.message}, and non-JSON response ${content.slice(0, 1000)}\n`);
|
|
237
|
+
// Stop checking.
|
|
238
|
+
abort = true;
|
|
248
239
|
resolve(true);
|
|
249
240
|
};
|
|
250
241
|
});
|
|
@@ -255,44 +246,52 @@ exports.netWatch = async (isForever, intervalInSeconds, isCertTolerant = true) =
|
|
|
255
246
|
if (error.code && error.code.includes('ECONNREFUSED')) {
|
|
256
247
|
// Report this.
|
|
257
248
|
console.log(`${logStart}no connection`);
|
|
249
|
+
// Stop checking.
|
|
250
|
+
abort = true;
|
|
258
251
|
}
|
|
259
252
|
// Otherwise, if it was a DNS failure:
|
|
260
253
|
else if (error.code && error.code.includes('ENOTFOUND')) {
|
|
261
254
|
// Report this.
|
|
262
255
|
console.log(`${logStart}no domain name resolution`);
|
|
256
|
+
// Stop checking.
|
|
257
|
+
abort = true;
|
|
263
258
|
}
|
|
264
259
|
// Otherwise, if it was any other error with a message:
|
|
265
260
|
else if (error.message) {
|
|
266
261
|
// Report this.
|
|
267
262
|
console.log(`ERROR: ${logStart}got error message ${error.message.slice(0, 200)}`);
|
|
263
|
+
// Stop checking.
|
|
264
|
+
abort = true;
|
|
268
265
|
}
|
|
269
266
|
// Otherwise, i.e. if it was any other error with no message:
|
|
270
267
|
else {
|
|
271
268
|
// Report this.
|
|
272
269
|
console.log(`ERROR: ${logStart}got an error with no message`);
|
|
270
|
+
// Stop checking.
|
|
271
|
+
abort = true;
|
|
273
272
|
}
|
|
274
273
|
resolve(true);
|
|
275
274
|
})
|
|
276
|
-
// Finish sending the job request
|
|
277
|
-
.end(
|
|
278
|
-
agentPW
|
|
279
|
-
})
|
|
275
|
+
// Finish sending the job request.
|
|
276
|
+
.end(JSON.stringify({
|
|
277
|
+
agentPW
|
|
278
|
+
}));
|
|
280
279
|
}
|
|
281
280
|
// If requesting a job throws an error:
|
|
282
281
|
catch(error) {
|
|
283
|
-
// Abort the watch.
|
|
284
|
-
abort = true;
|
|
285
282
|
// Report this.
|
|
286
283
|
console.log(`ERROR requesting a network job (${error.message})`);
|
|
284
|
+
// Stop checking.
|
|
285
|
+
abort = true;
|
|
287
286
|
resolve(true);
|
|
288
287
|
}
|
|
289
288
|
});
|
|
290
289
|
}
|
|
291
|
-
console.log(
|
|
290
|
+
console.log(`Watching ${abort ? 'aborted' : 'complete'}`);
|
|
292
291
|
}
|
|
293
|
-
// Otherwise, i.e. if the job
|
|
292
|
+
// Otherwise, i.e. if the job or report URL does not exist or is invalid:
|
|
294
293
|
else {
|
|
295
294
|
// Report this.
|
|
296
|
-
console.log('ERROR:
|
|
295
|
+
console.log('ERROR: Job or report URL does not exist or is invalid');
|
|
297
296
|
}
|
|
298
297
|
};
|
package/package.json
CHANGED
package/procs/doActs.js
CHANGED
|
@@ -49,21 +49,24 @@ const waits = Number.parseInt(process.env.WAITS) || 0;
|
|
|
49
49
|
// Time limits in seconds on tools, accounting for page reloads by 6 Testaro tests.
|
|
50
50
|
const timeLimits = {
|
|
51
51
|
alfa: 30,
|
|
52
|
+
aslint: 45,
|
|
52
53
|
ed11y: 30,
|
|
54
|
+
htmlcs: 30,
|
|
53
55
|
ibm: 30,
|
|
54
|
-
|
|
56
|
+
nuVal: 30,
|
|
57
|
+
nuVnu: 20,
|
|
58
|
+
qualWeb: 45,
|
|
59
|
+
testaro: 150 + Math.round(6 * waits / 1000),
|
|
60
|
+
wax: 25
|
|
55
61
|
};
|
|
56
62
|
// Timeout multiplier.
|
|
57
63
|
const timeoutMultiplier = Number.parseFloat(process.env.TIMEOUT_MULTIPLIER) || 1;
|
|
58
64
|
|
|
59
65
|
// FUNCTIONS
|
|
60
66
|
|
|
61
|
-
// Sends a notice to
|
|
67
|
+
// Sends a notice to a network observer.
|
|
62
68
|
const tellServer = (report, messageParams, logMessage) => {
|
|
63
|
-
const
|
|
64
|
-
const observerURL = typeof serverID === 'number'
|
|
65
|
-
? process.env[`NETWATCH_URL_${serverID}_OBSERVE`]
|
|
66
|
-
: '';
|
|
69
|
+
const observerURL = process.env.NETWATCH_URL_OBSERVE;
|
|
67
70
|
if (observerURL) {
|
|
68
71
|
const whoParams = `agent=${agent}&jobID=${report.id || ''}`;
|
|
69
72
|
const wholeURL = `${observerURL}?${whoParams}&${messageParams}`;
|
|
@@ -306,7 +309,6 @@ exports.doActs = async (report, opts = {}) => {
|
|
|
306
309
|
type,
|
|
307
310
|
which
|
|
308
311
|
});
|
|
309
|
-
console.log(`${message} (observer notified)`);
|
|
310
312
|
}
|
|
311
313
|
catch (error) {
|
|
312
314
|
console.log(`${message} (observer notification failed: ${errorStart(error)})`);
|
package/run.js
CHANGED
|
@@ -52,6 +52,8 @@ exports.doJob = async (job, opts = {}) => {
|
|
|
52
52
|
}
|
|
53
53
|
// Otherwise, i.e. if it is valid:
|
|
54
54
|
else {
|
|
55
|
+
// Report this.
|
|
56
|
+
console.log(`Starting job ${job.id} (${job.target.what})`);
|
|
55
57
|
// Add initialized job data to the report.
|
|
56
58
|
const startTime = new Date();
|
|
57
59
|
report.jobData = {
|
package/tests/nuVal.js
CHANGED
|
@@ -50,8 +50,8 @@ exports.reporter = async (page, report, actIndex) => {
|
|
|
50
50
|
const fetchOptions = {
|
|
51
51
|
method: 'post',
|
|
52
52
|
headers: {
|
|
53
|
-
'
|
|
54
|
-
'
|
|
53
|
+
'user-agent': 'Mozilla/5.0',
|
|
54
|
+
'content-type': 'text/html; charset=utf-8'
|
|
55
55
|
},
|
|
56
56
|
body: testTarget
|
|
57
57
|
};
|