Przeglądaj źródła

Writing the API (workin in progress)

Gaël Métais 10 lat temu
rodzic
commit
e18cd87a07

+ 4 - 3
Gruntfile.js

@@ -36,7 +36,8 @@ module.exports = function(grunt) {
                 'lib/**/*.js',
                 'lib/**/*.js',
                 'app/nodeControllers/*.js',
                 'app/nodeControllers/*.js',
                 'app/public/scripts/*.js',
                 'app/public/scripts/*.js',
-                'phantomas_custom/**/*.js'
+                'phantomas_custom/**/*.js',
+                'test/**/*.js'
             ]
             ]
         },
         },
         clean: {
         clean: {
@@ -70,13 +71,13 @@ module.exports = function(grunt) {
                 options: {
                 options: {
                     reporter: 'spec',
                     reporter: 'spec',
                 },
                 },
-                src: ['coverage/test/api/*.js']
+                src: ['coverage/test/api/*.js', 'coverage/test/server/*.js']
             },
             },
             'test-current-work': {
             'test-current-work': {
                 options: {
                 options: {
                     reporter: 'spec',
                     reporter: 'spec',
                 },
                 },
-                src: ['coverage/test/api/yellowlabtoolsTest.js']
+                src: ['coverage/test/server/runsQueueTest.js']
             },
             },
             coverage: {
             coverage: {
                 options: {
                 options: {

+ 0 - 66
app/lib/testQueue.js

@@ -1,66 +0,0 @@
-/**
- * Creation of a queue and it's worker function
- */
-
-var util                = require('util');
-var EventEmitter        = require('events').EventEmitter;
-var async               = require('async');
-var phantomasWrapper    = require('./phantomasWrapper');
-
-
-var testQueue = function() {
-    'use strict';
-
-    var currentTask = null;
-    var self = this;
-
-    var queue = async.queue(function(task, callback) {
-        currentTask = task;
-
-        console.log('Starting test ' + task.testId);
-        
-        phantomasWrapper.execute(task, function(err, json, results) {
-            console.log('Test ' + task.testId + ' complete');
-            currentTask = null;
-            callback(err, json, results);
-            self.emit('queueMoving');
-        });
-    });
-
-    
-    // Use this method to add a test to the queue
-    this.push = queue.push;
-
-    
-    // Gives the position of a task in the queue
-    // Returns 0 if it is the current running task
-    // Returns -1 if not found
-    this.indexOf = function(testId) {
-        if (currentTask && currentTask.testId === testId) {
-            return 0;
-        }
-
-        var position = -1;
-        if (queue.length() > 0) {
-            queue.tasks.forEach(function(task, index) {
-                if (task.data.testId === testId) {
-                    position = index + 1;
-                }
-            });
-        }
-        return position;
-    };
-
-    this.testComplete = function(testId) {
-        self.emit('testComplete', testId);
-    };
-
-    this.testFailed = function(testId) {
-        self.emit('testFailed', testId);
-    };
-};
-
-// extend the EventEmitter class
-util.inherits(testQueue, EventEmitter);
-
-module.exports = new testQueue();

+ 22 - 0
bin/server.js

@@ -0,0 +1,22 @@
+// Config file
+var settings                = require('../server_config/settings.json');
+
+var express                 = require('express');
+var app                     = express();
+var server                  = require('http').createServer(app);
+var bodyParser              = require('body-parser');
+var compress                = require('compression');
+
+app.use(compress());
+app.use(bodyParser.urlencoded({ extended: false }));
+
+
+// Initialize the controllers
+var apiController           = require('../lib/server/controllers/apiController')(app);
+var uiController            = require('../lib/server/controllers/uiController')(app);
+
+
+// Launch the server
+server.listen(settings.serverPort, function() {
+    console.log('Listening on port %d', server.address().port);
+});

+ 86 - 0
lib/server/controllers/apiController.js

@@ -0,0 +1,86 @@
+var debug           = require('debug')('ylt:server');
+
+var runsQueue       = require('../datastores/runsQueue');
+var runsDatastore   = require('../datastores/runsDatastore');
+
+
+function ApiController(app) {
+    'use strict';
+
+    // Retrieve the list of all runs
+    /*app.get('/runs', function(req, res) {
+        // NOT YET
+    });*/
+
+    // Create a new run
+    app.post('/runs', function(req, res) {
+
+        // Grab the test parameters
+        var run = {
+            // Generate a random run ID
+            _id: (Date.now()*1000 + Math.round(Math.random()*1000)).toString(36),
+            params: {
+                url: req.body.url,
+                waitForResponse: req.body.waitForResponse || true
+            }
+        };
+
+        // Add test to the testQueue
+        debug('Adding test %s to the queue', run._id);
+        var queuing = runsQueue.push(run._id);
+
+        
+        // Save the run to the datastore
+        var position = runsQueue.getPosition(run._id);
+        run.status = {
+            statusCode: (position === 0) ? STATUS_RUNNING : STATUS_AWAITING,
+            position: position
+        };
+        runsDatastore.add(run);
+
+
+        // Listening for position updates
+        queuing.progress(function(position) {
+            var savedRun = runsDatastore.get(run._id);
+            savedRun.status = {
+                statusCode: STATUS_AWAITING,
+                position: position
+            };
+            runsDatastore.update(savedRun);
+        });
+
+
+        queuing.then(function() {
+            
+        });
+
+        // The user doesn't not want to wait for the response
+        if (!params.waitForResponse) {
+
+            // Sending just the test id
+            res.setHeader('Content-Type', 'application/javascript');
+            res.send(JSON.stringify({
+                testId: testId
+            }));
+        }
+    });
+
+    // Retrive one run by id
+    app.get('/run/:id', function(req, res) {
+
+    });
+
+    // Delete one run by id
+    /*app.delete('/run/:id', function(req, res) {
+        // NOT YET
+    });*/
+
+
+    var STATUS_AWAITING     = 'awaiting';
+    var STATUS_RUNNING      = 'running';
+    var STATUS_DONE         = 'done';
+    var STATUS_FAILED       = 'failed';
+
+}
+
+module.exports = ApiController;

+ 0 - 0
lib/server/controllers/uiController.js


+ 9 - 0
lib/server/datastores/resultsDatastore.js

@@ -0,0 +1,9 @@
+
+
+function ResultsDatastore() {
+    'use strict';
+
+    
+}
+
+module.exports = ResultsDatastore;

+ 36 - 0
lib/server/datastores/runsDatastore.js

@@ -0,0 +1,36 @@
+
+
+function RunsDatastore() {
+    'use strict';
+
+    // NOT PERSISTING RUNS
+    // For the moment, maybe one day
+    var runs = {};
+
+
+    this.add = function(run) {
+        runs[run._id] = run;
+    };
+
+    this.get = function(runId) {
+        return runs[runId];
+    };
+    
+    this.update = function(run) {
+        runs[run._id] = run;
+    };
+
+    this.delete = function(runId) {
+        delete runs[runId];
+    };
+
+    this.list = function() {
+        var runsArray = [];
+        Object.keys(runs).forEach(function(key) {
+            runsArray.push(runs[key]);
+        });
+        return runsArray;
+    };
+}
+
+module.exports = RunsDatastore;

+ 73 - 0
lib/server/datastores/runsQueue.js

@@ -0,0 +1,73 @@
+var Q = require('q');
+
+
+function RunsQueue() {
+    'use strict';
+
+    var queue = [];
+
+
+    this.push = function(runId) {
+        var deferred = Q.defer();
+
+        if (queue.length === 0) {
+            
+            // The queue is empty, let's run immediatly
+            queue.push({
+                runId: runId
+            });
+            
+            deferred.resolve();
+
+        } else {
+            
+            queue.push({
+                runId: runId,
+                positionChangedCallback: function(position) {
+                    deferred.notify(position);
+                },
+                itIsTimeCallback: function() {
+                    deferred.resolve();
+                }
+            });
+        }
+
+        return deferred.promise;
+    };
+
+
+    this.getPosition = function(runId) {
+        // Position 0 means it's a work in progress (a run is removed AFTER it is finished, not before)
+        var position = -1;
+
+        queue.some(function(run, index) {
+            if (run.runId === runId) {
+                position = index;
+                return true;
+            }
+            return false;
+        });
+
+        return position;
+    };
+
+
+    this.remove = function(runId) {
+        var position = this.getPosition(runId);
+        if (position >= 0) {
+            queue.splice(position, 1);
+        }
+
+        // Update other runs' positions
+        queue.forEach(function(run, index) {
+            if (index === 0 && run.itIsTimeCallback) {
+                run.itIsTimeCallback();
+            } else if (index > 0 && run.positionChangedCallback) {
+                run.positionChangedCallback(index);
+            }
+        });
+
+    };
+}
+
+module.exports = RunsQueue;

+ 1 - 1
package.json

@@ -14,7 +14,7 @@
     "body-parser": "~1.9.2",
     "body-parser": "~1.9.2",
     "compression": "~1.2.0",
     "compression": "~1.2.0",
     "debug": "^2.1.0",
     "debug": "^2.1.0",
-    "express": "~4.10.1",
+    "express": "~4.10.4",
     "phantomas": "1.7.0",
     "phantomas": "1.7.0",
     "socket.io": "~1.2.0"
     "socket.io": "~1.2.0"
   },
   },

+ 6 - 6
test/api/phantomasWrapperTest.js

@@ -23,9 +23,9 @@ describe('phantomasWrapper', function() {
             data.should.have.a.property('generator');
             data.should.have.a.property('generator');
             data.generator.should.contain('phantomas');
             data.generator.should.contain('phantomas');
             data.should.have.a.property('url').that.equals(url);
             data.should.have.a.property('url').that.equals(url);
-            data.should.have.a.property('metrics').that.is.an('object').not.empty;
-            data.should.have.a.property('offenders').that.is.an('object').not.empty;
-            data.offenders.should.have.a.property('javascriptExecutionTree').that.is.a('array').not.empty;
+            data.should.have.a.property('metrics').that.is.an('object').not.empty();
+            data.should.have.a.property('offenders').that.is.an('object').not.empty();
+            data.offenders.should.have.a.property('javascriptExecutionTree').that.is.a('array').not.empty();
 
 
             done();
             done();
         }).fail(function(err) {
         }).fail(function(err) {
@@ -73,9 +73,9 @@ describe('phantomasWrapper', function() {
             data.should.have.a.property('generator');
             data.should.have.a.property('generator');
             data.generator.should.contain('phantomas');
             data.generator.should.contain('phantomas');
             data.should.have.a.property('url').that.equals(url);
             data.should.have.a.property('url').that.equals(url);
-            data.should.have.a.property('metrics').that.is.an('object').not.empty;
-            data.should.have.a.property('offenders').that.is.an('object').not.empty;
-            data.offenders.should.have.a.property('javascriptExecutionTree').that.is.a('array').not.empty;
+            data.should.have.a.property('metrics').that.is.an('object').not.empty();
+            data.should.have.a.property('offenders').that.is.an('object').not.empty();
+            data.offenders.should.have.a.property('javascriptExecutionTree').that.is.a('array').not.empty();
 
 
             done();
             done();
         }).fail(function(err) {
         }).fail(function(err) {

+ 2 - 1
test/api/yellowlabtoolsTest.js

@@ -34,7 +34,7 @@ describe('yellowlabtools', function() {
         this.timeout(15000);
         this.timeout(15000);
 
 
         // Check if console.log is called
         // Check if console.log is called
-        sinon.spy(console, 'log')
+        sinon.spy(console, 'log');
 
 
         var url = 'http://localhost:8388/simple-page.html';
         var url = 'http://localhost:8388/simple-page.html';
 
 
@@ -75,6 +75,7 @@ describe('yellowlabtools', function() {
                     "offenders": ["body > h1[1]"]
                     "offenders": ["body > h1[1]"]
                 });
                 });
 
 
+                /*jshint expr: true*/
                 console.log.should.not.have.been.called;
                 console.log.should.not.have.been.called;
 
 
                 done();
                 done();

+ 41 - 0
test/server/runsDatastoreTest.js

@@ -0,0 +1,41 @@
+var should = require('chai').should();
+var runsDatastore = require('../../lib/server/datastores/runsDatastore');
+
+describe('runsDatastore', function() {
+    
+    var datastore = new runsDatastore();
+
+    var randomId = Math.round(Math.random() * 100000);
+
+    it('should accept a new run', function() {
+        datastore.should.have.a.property('add').that.is.a('function');
+
+        datastore.add({
+            _id: randomId,
+            otherData: 123456789
+        });
+    });
+
+    it('should have stored the run', function() {
+        datastore.should.have.a.property('get').that.is.a('function');
+
+        var run = datastore.get(randomId);
+
+        run.should.have.a.property('_id').that.equals(randomId);
+    });
+
+    it('should have exactly 1 run in the store', function() {
+        var runs = datastore.list();
+        runs.should.be.a('array');
+        runs.should.have.length(1);
+        runs[0].should.have.a.property('_id').that.equals(randomId);
+    });
+
+    it('should delete the run', function() {
+        datastore.delete(randomId);
+
+        var runs = datastore.list();
+        runs.should.be.a('array');
+        runs.should.have.length(0);
+    });
+});

+ 64 - 0
test/server/runsQueueTest.js

@@ -0,0 +1,64 @@
+var should = require('chai').should();
+var runsQueue = require('../../lib/server/datastores/runsQueue.js');
+
+describe('runsQueue', function() {
+
+    var queue = new runsQueue();
+    var cccRun = null;
+
+    it('should accept a new runId', function(done) {
+        queue.should.have.a.property('push').that.is.a('function');
+
+        var aaaRun = queue.push('aaa');
+        queue.push('bbb');
+
+        aaaRun.then(function() {
+            done();
+        });
+    });
+
+    it('should return the right positions', function() {
+        var aaaPosition = queue.getPosition('aaa');
+        aaaPosition.should.equal(0);
+
+        var bbbPosition = queue.getPosition('bbb');
+        bbbPosition.should.equal(1);
+
+        var cccPosition = queue.getPosition('ccc');
+        cccPosition.should.equal(-1);
+    });
+
+    it('should refresh runs\' positions', function(done) {
+        cccRun = queue.push('ccc');
+
+        cccRun.progress(function(position) {
+            position.should.equal(1);
+
+            var positionDoubleCheck = queue.getPosition('ccc');
+            positionDoubleCheck.should.equal(1);
+
+            done();
+        });
+
+        queue.remove('aaa');
+    });
+
+    it('should fulfill the promise when first in the line', function(done) {
+        cccRun.then(function() {
+            done();
+        });
+
+        queue.remove('bbb');
+    });
+
+    it('should not keep removed runs', function() {
+        var aaaPosition = queue.getPosition('aaa');
+        aaaPosition.should.equal(-1);
+
+        var bbbPosition = queue.getPosition('bbb');
+        bbbPosition.should.equal(-1);
+
+        var cccPosition = queue.getPosition('ccc');
+        cccPosition.should.equal(0);
+    });
+});

+ 0 - 23
test/server/testQueueTest.js

@@ -1,23 +0,0 @@
-var should = require('chai').should();
-var testQueue = require('../../app/lib/testQueue.js');
-
-describe('testQueue', function() {
-    
-    var url = 'http://www.not.existing';
-
-    it('should accept a new test with method push', function() {
-        testQueue.should.have.property('push').that.is.a('function');
-
-        var task = {
-            testId: 'aaaaa',
-            url: url,
-            options: {}
-        };
-
-        testQueue.push(task, function(err, json, results) {
-            done();
-        });
-
-    });
-
-});