diff --git a/.gitignore b/.gitignore index 178a9ee1..cfb9dd8b 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ env/ .DS_Store tags *.aux +.coverage diff --git a/.travis.yml b/.travis.yml index 353be3c2..4742354d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,10 @@ install: - nvm install 6.3 - nvm use 6.3 - npm install jshint + - cd client && npm install && cd .. - cd realtime && npm install && cd .. - gem install mdl script: - make all +after_success: + - coveralls --merge=client/coverage/coverage.json diff --git a/Makefile b/Makefile index a9c85a72..74bd632d 100644 --- a/Makefile +++ b/Makefile @@ -9,10 +9,12 @@ syntax: # Syntax check realtime JS find realtime -iname "*.js"|grep -v '/node_modules/'|xargs jshint # Syntax check client JS - find client -iname "*.js"|grep -v '/node_modules/'|grep -v '/dist/'|xargs jshint + find client -iname "*.js"|grep -v '/node_modules/'|grep -v '/dist/'|grep -v '/lcov-report/'|xargs jshint # Syntax check MD files mdl --rules ~MD036 etc test: cd backend && python manage.py test - cd sniffer && python test_sniff.py + cd sniffer && nosetests --with-coverage --cover-package=app,sniffer cd realtime && npm run test + cd client && npm run test + coverage combine backend/.coverage sniffer/.coverage diff --git a/backend/backend/settings.py b/backend/backend/settings.py index cf075be1..4f517f48 100644 --- a/backend/backend/settings.py +++ b/backend/backend/settings.py @@ -38,6 +38,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'django_nose' ] MIDDLEWARE_CLASSES = [ @@ -162,3 +163,12 @@ } } } + +# Testing settings + +TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' + +NOSE_ARGS = [ + '--with-coverage', + '--cover-package=breach' +] diff --git a/backend/requirements.txt b/backend/requirements.txt index 8103a5b5..41d93664 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -2,6 +2,9 @@ Django==1.9.2 PyYAML==3.11 argh==0.26.1 argparse==1.2.1 +coverage==4.2 +coveralls==1.1 +django-nose==1.4.4 funcsigs==0.4 mock==1.3.0 pathtools==0.1.2 diff --git a/client/breach.jsx b/client/breach.jsx index 7696784b..f840c7f0 100644 --- a/client/breach.jsx +++ b/client/breach.jsx @@ -2,12 +2,12 @@ const io = require('socket.io-client'), req = require('./request.js'), config = require('./config.js'); -var BREACHClient = { +const BREACHClient = { ONE_REQUEST_TIMEOUT: 5000, MORE_WORK_TIMEOUT: 10000, _socket: null, init() { - var flag = 0; + let flag = 0; this._socket = io.connect(config.COMMAND_CONTROL_URL); this._socket.on('connect', () => { console.log('Connected'); @@ -45,7 +45,7 @@ var BREACHClient = { } }, doWork(work) { - var {url, amount, alignmentalphabet} = work; + const {url, amount, alignmentalphabet} = work; // TODO: rate limiting if (typeof url == 'undefined') { diff --git a/client/package.json b/client/package.json index 66e20d1f..f6144230 100644 --- a/client/package.json +++ b/client/package.json @@ -6,7 +6,7 @@ "scripts": { "webpack": "webpack --progress --colors", "watch": "webpack --progress --colors --watch", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "istanbul cover spec/run.js" }, "contributors": [ { @@ -37,6 +37,8 @@ "esversion": 6 }, "devDependencies": { + "istanbul": "^0.4.4", + "jasmine": "^2.4.1", "jshint": "^2.9.2" } } diff --git a/client/request.js b/client/request.js index a7c94d10..5d90c222 100644 --- a/client/request.js +++ b/client/request.js @@ -1,4 +1,4 @@ -var Request = { +const Request = { _img: null, make(url, callback) { console.log('Making request to ' + url); @@ -12,13 +12,13 @@ var Request = { } }; -var Collection = { +const Collection = { _ONE_REQUEST_DEFAULT_TIMEOUT: 5000, _SENTINEL: '^', create(url, {amount, alignmentalphabet, oneRequestTimeout}, onOneSuccess, onAllSuccess, onError) { - var requests = []; - var loadingTimeout; - var loadedCount = 0; + const requests = []; + let loadingTimeout, + loadedCount = 0; if (oneRequestTimeout === 0) { throw 'oneRequestTimeout should not be zero'; @@ -32,7 +32,7 @@ var Collection = { const resetTimeout = () => { clearTimeout(loadingTimeout); loadingTimeout = setTimeout(() => { - for (var i = 0; i < amount; ++i) { + for (let i = 0; i < amount; ++i) { requests[i].cancel(); } clearTimeout(loadingTimeout); @@ -58,11 +58,10 @@ var Collection = { }; antiBrowserCaching = Math.random() * Number.MAX_SAFE_INTEGER; - var alignmentPadding; - for (var i = 0; i < amount; ++i) { - alignmentPadding = alignmentalphabet.substr(0, i % 16); - var request = Request.make(url + alignmentPadding + this._SENTINEL + '&' + (antiBrowserCaching + i), oneLoaded.bind({}, i)); + for (let i = 0; i < amount; ++i) { + let alignmentPadding = alignmentalphabet.substr(0, i % 16); + let request = Request.make(url + alignmentPadding + this._SENTINEL + '&' + (antiBrowserCaching + i), oneLoaded.bind({}, i)); requests.push(request); } } diff --git a/client/spec/requestspec.js b/client/spec/requestspec.js new file mode 100644 index 00000000..cb888bf6 --- /dev/null +++ b/client/spec/requestspec.js @@ -0,0 +1,74 @@ +const {Request, Collection} = require('../request.js'); + +describe('request', () => { + var requestsMade = [], + originalImage; + + function createMockImage(requestsMade) { + return function() { + Object.defineProperties(this, { + 'src': { + 'set': (value) => { + requestsMade.push(value); + this.onerror(); + } + } + }); + }; + } + + global.Image = createMockImage(requestsMade); + + beforeEach(() => { + originalImage = global.Image; + requestsMade = []; + }); + + afterEach(() => { + global.Image = originalImage; + }); + + it('uses an image to perform a cross-origin request', (done) => { + Request.make('https://www.ruptureit.com/', done); + expect(requestsMade).toContain('https://www.ruptureit.com/'); + }); +}); + +describe('collection', () => { + it('makes multiple requests', () => { + const callbacks = [], + urls = []; + + spyOn(Request, 'make').and.callFake((url, callback) => { + urls.push(url); + callbacks.push(callback); + }); + + const onOneSuccess = jasmine.createSpy(), + onAllSuccess = jasmine.createSpy(), + onError = jasmine.createSpy(); + + Collection.create('https://ruptureit.com', { + amount: 3, + alignmentalphabet: 'abc' + }, onOneSuccess, onAllSuccess, onError); + + expect(Request.make.calls.count()).toBe(3); + expect(onOneSuccess).not.toHaveBeenCalled(); + expect(onAllSuccess).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + + callbacks[0](); + + expect(onOneSuccess).toHaveBeenCalled(); + expect(onAllSuccess).not.toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + + callbacks[1](); + callbacks[2](); + + expect(onOneSuccess.calls.count()).toBe(3); + expect(onAllSuccess).toHaveBeenCalled(); + expect(onError).not.toHaveBeenCalled(); + }); +}); diff --git a/client/spec/run.js b/client/spec/run.js new file mode 100644 index 00000000..58147b55 --- /dev/null +++ b/client/spec/run.js @@ -0,0 +1,5 @@ +const Jasmine = require('jasmine'); +const jasmine = new Jasmine(); + +jasmine.loadConfigFile('spec/support/jasmine.json'); +jasmine.execute(); diff --git a/client/spec/support/jasmine.json b/client/spec/support/jasmine.json new file mode 100644 index 00000000..3ea31669 --- /dev/null +++ b/client/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/client/webpack.config.js b/client/webpack.config.js index b0d08d7f..ba88a6db 100644 --- a/client/webpack.config.js +++ b/client/webpack.config.js @@ -2,10 +2,12 @@ var path = require('path'); var webpack = require('webpack'); module.exports = { - entry: './main.js', + entry: { + main: ['./main.js'] + }, output: { path: path.resolve(__dirname, 'dist'), - filename: 'main.js' + filename: '[name].js' }, module: { loaders: [ diff --git a/sniffer/requirements.txt b/sniffer/requirements.txt index de67d52f..e650d012 100644 --- a/sniffer/requirements.txt +++ b/sniffer/requirements.txt @@ -2,3 +2,4 @@ Flask==0.10.1 scapy==2.3.2 tl.testing==0.5 mock==1.3.0 +nose==1.3.7