it('file: binary files should also work', function(done){ specHelper.apiTest.get('/public/' + 'logo/actionHero.png', 0, {}, function(response, json){ response.statusCode.should.equal(200); response.body.length.should.be.within(136836, 136920); done(); }); });
specHelper.prepare(0, function(api){ apiObj = specHelper.cleanAPIObject(api); //TODO: Why does travis-ci require the connection to be seeded like this? // It works locally on OSX and Ubuntu specHelper.apiTest.get('/public/' + 'simple.html', 0, {}, function(response, json){}); done(); })
it('file: index page should be served when requesting a path', function(done){ specHelper.apiTest.get('/public/', 0, {}, function(response, json){ response.statusCode.should.equal(200); response.body.should.be.a('string'); done(); }); });
it('I should not see files outside of the public dir', function(done){ specHelper.apiTest.get('/public/' + '?file=../config.json', 0, {}, function(response, json){ response.statusCode.should.equal(404); response.body.should.equal(apiObj.config.general.flatFileNotFoundMessage); done(); }); });
it('file: ?filename should work like a path', function(done){ specHelper.apiTest.get('/public/' + '?file=simple.html', 0, {}, function(response, json){ response.statusCode.should.equal(200); response.body.should.equal('<h1>ActionHero</h1>\\nI am a flat file being served to you via the API from ./public/simple.html<br />'); done(); }); });
it('real actions do not have an error response', function(done){ specHelper.apiTest.get('/api/status', 0, {}, function(response, json){ // response.body.error.should.equal('OK') should.not.exist(json.error); done(); }); });
it('status codes can be set for errors', function(done){ specHelper.apiTest.del('/statusTestAction', 0, {key: 'bannana'}, function(response, json){ json.error.should.eql('key != value'); response.statusCode.should.eql(402); done(); }); });
it('random numbers work initially', function(done){ specHelper.apiTest.get('/randomNumber', 0, {}, function(response, json){ should.not.exist(json.error); json.randomNumber.should.be.within(0,1); done(); }); });
it('params are ignored unless they are in the whitelist', function(done){ specHelper.apiTest.get('/testAction/?crazyParam123=something', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('testAction'); should.not.exist(json.requestorInformation.receivedParams['crazyParam123']); done(); }); });
it('the bad action should fail gracefully', function(done){ /** * @nullivex * This test in particular is interesting because of the way it expects * mocha to handle and rethrow the exception. * * Implementing grunt and grunt-mocha-test will run this code inside a domain * and the action runner also creates a domain so they are nested. In node ~0.8.9 * this seems to cause the exception to land in mocha's lap rather than being * rethrown to the child domain. * * I believe this works fine in node ~0.9.0 because they ironed out some of the * issues especially with nested domains and how exceptions are bubbled in those * environments there is an issue about this here: https://github.com/joyent/node/issues/4375 * * So, it is best just to ignore this test in node ~0.8.9 and below and it should * still pass in normal environments. */ if(9 < parseInt(process.version.split('.')[1],10)){ specHelper.apiTest.get('/api/badAction', 0, {} , function(response, json){ json.error.should.equal('Error: The server experienced an internal error'); done(); }); } else { done() } });
it('status code should still be 200 if everything is OK', function(done){ specHelper.apiTest.del('/statusTestAction', 0, {key: 'value'}, function(response, json){ json.good.should.eql(true); response.statusCode.should.eql(200); done(); }); });
it('unknwon actions are still unknwon', function(done){ specHelper.apiTest.get('/a_crazy_action', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('a_crazy_action') json.error.should.equal('Error: a_crazy_action is not a known action or that is not a valid apiVersion.') done(); }); });
setTimeout(function(){ specHelper.apiTest.get('/randomNumber', 0, {}, function(response, json){ apiObj.actions.actions.randomNumber["1"].description.should.equal("HACK"); json.randomNumber.should.equal("not a number!"); done(); }); }, 1001 * 3); //file read timer is 1 second; time to notice the change + 3x time to reaload API
it('Server basic response should be JSON and have basic data', function(done){ specHelper.apiTest.get('', 0, {}, function(response, json){ json.should.be.an.instanceOf(Object); json.requestorInformation.should.be.an.instanceOf(Object); done(); }); });
it('regexp matches will still work with parmas with periods and other wacky chars', function(done){ specHelper.apiTest.get('/c/key/log_me-in.com$123.jpg', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('cacheTest'); json.requestorInformation.receivedParams.value.should.equal('log_me-in.com$123.jpg'); done(); }); });
it('should respond to HEAD requets just like GET, but with no body', function(done){ specHelper.apiTest.head('/randomNumber', 0, {}, function(response, json){ response.statusCode.should.eql(200); should.not.exist(json); done(); }); });
it('will not change header information if there is a connection.error', function(done){ specHelper.apiTest.get('/mimeTestAction', 0, {}, function(response, json){ response.headers['content-type'].should.equal('application/json'); json.error.should.equal("Error: key is a required parameter for this action"); done(); }); });
it('limit and offset should have defaults', function(done){ specHelper.apiTest.get('/', 0, {}, function(response, json){ json.requestorInformation.receivedParams.limit.should.equal(100) json.requestorInformation.receivedParams.offset.should.equal(0) done(); }); });
it('duplicate cookies should be removed (in favor of the last set)', function(done){ specHelper.apiTest.del('/headerTestAction', 0, {}, function(response, json){ response.statusCode.should.eql(200); response.headers['thing'].should.eql("C"); done(); }); });
it('regexp matches will provide proper variables', function(done){ specHelper.apiTest.post('/login/123', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('login'); json.requestorInformation.receivedParams.userID.should.equal('123'); done(); }); });
it('Routes should be mapped for GET (complex)', function(done){ specHelper.apiTest.get('/user/1234', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('user') json.requestorInformation.receivedParams.userID.should.equal('1234') done(); }); });
it('Routes should be mapped for DELETE', function(done){ specHelper.apiTest.del('/user/1234?key=value', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('user') json.requestorInformation.receivedParams.userID.should.equal('1234') json.requestorInformation.receivedParams.key.should.equal('value') done(); }); });
it("can create callbackcks on connection creation", function(done){ rawApi.connections.createCallbacks.push(function(c){ done(); }); specHelper.apiTest.get('/randomNumber', 0, {}, function(response, json){ // }); });
it('regexp match failures will be rejected', function(done){ specHelper.apiTest.post('/login/1234', 0, {}, function(response, json){ json.error.should.equal("Error: login is not a known action or that is not a valid apiVersion."); json.requestorInformation.receivedParams.action.should.equal('login'); should.not.exist(json.requestorInformation.receivedParams.userID); done(); }); });
it('but duplicate set-cookie requests should be allowed', function(done){ specHelper.apiTest.del('/headerTestAction', 0, {}, function(response, json){ response.statusCode.should.eql(200); response.headers['set-cookie'].length.should.eql(2); response.headers['set-cookie'][1].should.eql('value 1'); response.headers['set-cookie'][0].should.eql('value 2'); done(); }); });
it('should respond to OPTIONS with only HTTP headers', function(done){ specHelper.apiTest.options('/x', 0, {}, function(response, json){ response.statusCode.should.eql(200); response.headers['access-control-allow-methods'].should.equal('HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE'); response.headers['access-control-allow-origin'].should.equal('*'); response.headers['content-length'].should.equal('0'); done(); }); });
it('should respond to TRACE with parsed params recieved', function(done){ specHelper.apiTest.trace('/api/x', 0, {key: 'someKey', value: 'someValue'}, function(response, json){ response.statusCode.should.eql(200); json.receivedParams.action.should.equal('x'); json.receivedParams.key.should.equal('someKey'); json.receivedParams.value.should.equal('someValue'); done(); }); });
it('route params trump explicit params', function(done){ specHelper.apiTest.get('/search/SeachTerm/limit/123/offset/456?term=otherSearchTerm&limit=0&offset=0', 0, {}, function(response, json){ json.requestorInformation.receivedParams.action.should.equal('search') json.requestorInformation.receivedParams.term.should.equal('SeachTerm') json.requestorInformation.receivedParams.limit.should.equal(123) json.requestorInformation.receivedParams.offset.should.equal(456) done(); }); });
it('I can define an action preProcessor and it can append the connection', function(done){ rawApi.actions.preProcessors.push(function(connection, actionTemplate, next){ connection.response._preProcessorNote = 'note' next(connection, true); }); specHelper.apiTest.get('/api/randomNumber', 0, {}, function(response, json){ json._preProcessorNote.should.equal('note'); done(); }); });
it("postProcessors can append the connection", function(done){ rawApi.actions.postProcessors.push(function(connection, actionTemplate, toRender, next){ connection.response._postProcessorNote = "note" next(connection, true); }); specHelper.apiTest.get('/randomNumber', 0, {}, function(response, json){ json._postProcessorNote.should.equal("note"); done(); }); })