568 lines
17 KiB
JavaScript
568 lines
17 KiB
JavaScript
var should = require('chai').should();
|
|
var request = require('request');
|
|
var Q = require('q');
|
|
|
|
var config = {
|
|
"authorizedKeys": {
|
|
"1234567890": "contact@gaelmetais.com"
|
|
}
|
|
};
|
|
|
|
var serverUrl = 'http://localhost:8387';
|
|
var wwwUrl = 'http://localhost:8388';
|
|
|
|
describe('api', function() {
|
|
|
|
|
|
var syncRunResultUrl;
|
|
var asyncRunId;
|
|
var screenshotUrl;
|
|
|
|
|
|
it('should refuse a query with an invalid key', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: wwwUrl + '/simple-page.html',
|
|
waitForResponse: false
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': 'invalid'
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 401) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should fail without an URL when asynchronous', function(done) {
|
|
this.timeout(15000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: ''
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 400) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should fail without an URL when synchronous', function(done) {
|
|
this.timeout(15000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: '',
|
|
waitForResponse: true
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 400) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should launch a synchronous run', function(done) {
|
|
this.timeout(15000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: wwwUrl + '/simple-page.html',
|
|
waitForResponse: true,
|
|
screenshot: true
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 302) {
|
|
|
|
response.headers.should.have.a.property('location').that.is.a('string');
|
|
syncRunResultUrl = response.headers.location;
|
|
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should return the rules only', function(done) {
|
|
this.timeout(15000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: wwwUrl + '/simple-page.html',
|
|
waitForResponse: true,
|
|
partialResult: 'rules'
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 302) {
|
|
|
|
response.headers.should.have.a.property('location').that.is.a('string');
|
|
response.headers.location.should.contain('/rules');
|
|
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should retrieve the results for the synchronous run', function(done) {
|
|
this.timeout(15000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + syncRunResultUrl,
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.should.have.a.property('runId').that.is.a('string');
|
|
body.should.have.a.property('params').that.is.an('object');
|
|
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
|
body.should.have.a.property('rules').that.is.an('object');
|
|
body.should.have.a.property('toolsResults').that.is.an('object');
|
|
|
|
// javascriptExecutionTree should only be filled if option jsTimeline is true
|
|
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
|
body.javascriptExecutionTree.should.deep.equal({});
|
|
|
|
// Check if the screenshot temporary file was correctly removed
|
|
body.params.options.should.not.have.a.property('screenshot');
|
|
// Check if the screenshot buffer was correctly removed
|
|
body.should.not.have.a.property('screenshotBuffer');
|
|
// Check if the screenshot url is here
|
|
body.should.have.a.property('screenshotUrl');
|
|
body.screenshotUrl.should.equal('/api/results/' + body.runId + '/screenshot.jpg');
|
|
|
|
screenshotUrl = body.screenshotUrl;
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should launch a run without waiting for the response', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: wwwUrl + '/simple-page.html',
|
|
waitForResponse: false,
|
|
jsTimeline: true
|
|
},
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
asyncRunId = body.runId;
|
|
asyncRunId.should.be.a('string');
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should respond run status: running', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/runs/' + asyncRunId,
|
|
json: true,
|
|
headers: {
|
|
'X-Api-Key': Object.keys(config.authorizedKeys)[0]
|
|
}
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.runId.should.equal(asyncRunId);
|
|
body.status.should.deep.equal({
|
|
statusCode: 'running'
|
|
});
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should accept up to 10 anonymous runs to the API', function(done) {
|
|
this.timeout(5000);
|
|
|
|
function launchRun() {
|
|
var deferred = Q.defer();
|
|
|
|
request({
|
|
method: 'POST',
|
|
url: serverUrl + '/api/runs',
|
|
body: {
|
|
url: wwwUrl + '/simple-page.html',
|
|
waitForResponse: false
|
|
},
|
|
json: true
|
|
}, function(error, response, body) {
|
|
|
|
lastRunId = body.runId;
|
|
|
|
if (error) {
|
|
deferred.reject(error);
|
|
} else {
|
|
deferred.resolve(response, body);
|
|
}
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
launchRun()
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
|
|
.then(function(response, body) {
|
|
|
|
// Here should still be ok
|
|
response.statusCode.should.equal(200);
|
|
|
|
launchRun()
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
.then(launchRun)
|
|
|
|
.then(function(response, body) {
|
|
|
|
// It should fail now
|
|
response.statusCode.should.equal(429);
|
|
done();
|
|
|
|
})
|
|
.fail(function(error) {
|
|
done(error);
|
|
});
|
|
|
|
}).fail(function(error) {
|
|
done(error);
|
|
});
|
|
|
|
});
|
|
|
|
|
|
it('should respond 404 to unknown runId', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/runs/unknown',
|
|
json: true
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 404) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should respond 404 to unknown result', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/unknown',
|
|
json: true
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 404) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should respond status complete to the first run', function(done) {
|
|
this.timeout(12000);
|
|
|
|
function checkStatus() {
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/runs/' + asyncRunId,
|
|
json: true
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.runId.should.equal(asyncRunId);
|
|
|
|
if (body.status.statusCode === 'running') {
|
|
setTimeout(checkStatus, 250);
|
|
} else if (body.status.statusCode === 'complete') {
|
|
done();
|
|
} else {
|
|
done(body.status.statusCode);
|
|
}
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
}
|
|
|
|
checkStatus();
|
|
});
|
|
|
|
|
|
it('should find the result of the async run', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId,
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.should.have.a.property('runId').that.equals(asyncRunId);
|
|
body.should.have.a.property('params').that.is.an('object');
|
|
body.params.url.should.equal(wwwUrl + '/simple-page.html');
|
|
|
|
body.should.have.a.property('scoreProfiles').that.is.an('object');
|
|
body.scoreProfiles.should.have.a.property('generic').that.is.an('object');
|
|
body.scoreProfiles.generic.should.have.a.property('globalScore').that.is.a('number');
|
|
body.scoreProfiles.generic.should.have.a.property('categories').that.is.an('object');
|
|
|
|
body.should.have.a.property('rules').that.is.an('object');
|
|
|
|
body.should.have.a.property('toolsResults').that.is.an('object');
|
|
body.toolsResults.should.have.a.property('phantomas').that.is.an('object');
|
|
|
|
body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
|
|
body.javascriptExecutionTree.should.have.a.property('data').that.is.an('object');
|
|
body.javascriptExecutionTree.data.should.have.a.property('type').that.equals('main');
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should return the generic score object', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/generalScores',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
body.should.have.a.property('globalScore').that.is.a('number');
|
|
body.should.have.a.property('categories').that.is.an('object');
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should return the generic score object also', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/generic',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
body.should.have.a.property('globalScore').that.is.a('number');
|
|
body.should.have.a.property('categories').that.is.an('object');
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should not find an unknown score object', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/generalScores/unknown',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 404) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should return the rules', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/rules',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
var firstRule = body[Object.keys(body)[0]];
|
|
firstRule.should.have.a.property('policy').that.is.an('object');
|
|
firstRule.should.have.a.property('value').that.is.a('number');
|
|
firstRule.should.have.a.property('bad').that.is.a('boolean');
|
|
firstRule.should.have.a.property('abnormal').that.is.a('boolean');
|
|
firstRule.should.have.a.property('score').that.is.a('number');
|
|
firstRule.should.have.a.property('abnormalityScore').that.is.a('number');
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should return the javascript execution tree', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/javascriptExecutionTree',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.should.have.a.property('data').that.is.an('object');
|
|
body.data.should.have.a.property('type').that.equals('main');
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should return the phantomas results', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/' + asyncRunId + '/toolsResults/phantomas',
|
|
json: true,
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
|
|
body.should.have.a.property('metrics').that.is.an('object');
|
|
body.should.have.a.property('offenders').that.is.an('object');
|
|
|
|
done();
|
|
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should retrieve the screenshot', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + screenshotUrl
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 200) {
|
|
response.headers['content-type'].should.equal('image/jpeg');
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
|
|
it('should fail on a unexistant screenshot', function(done) {
|
|
this.timeout(5000);
|
|
|
|
request({
|
|
method: 'GET',
|
|
url: serverUrl + '/api/results/000000/screenshot.jpg'
|
|
}, function(error, response, body) {
|
|
if (!error && response.statusCode === 404) {
|
|
done();
|
|
} else {
|
|
done(error || response.statusCode);
|
|
}
|
|
});
|
|
});
|
|
|
|
});
|