From 19cac85c843839332fa911d900afd7fef70c5bea Mon Sep 17 00:00:00 2001 From: Dan Hough Date: Sat, 9 Aug 2014 14:06:44 +0100 Subject: [PATCH] Arrays are handled as expected by extends #23 --- lib/fluent.js | 14 +++--- lib/route.js | 2 +- package.json | 2 +- readme.md | 36 ++++++++++++++- tests/fluent.test.js | 107 ++++++++++++++++++++++++++++++++----------- 5 files changed, 123 insertions(+), 38 deletions(-) diff --git a/lib/fluent.js b/lib/fluent.js index 436b07a..35aac9b 100644 --- a/lib/fluent.js +++ b/lib/fluent.js @@ -6,7 +6,7 @@ function FluentInterface(server, o) { throw new Error('Sorry, but modified routes cannot yet create new routes after their response. This is planned for a future version of Interfake.'); } - function throwModifiesNotSupportedError () { + function throwExtendsNotSupportedError () { throw new Error('Sorry, but modified routes cannot yet modify existing routes after their response. This is planned for a future version of Interfake.'); } @@ -48,11 +48,11 @@ function FluentInterface(server, o) { post: throwCreatesNotSupportedError, delete: throwCreatesNotSupportedError }, - modifies: { - get: throwModifiesNotSupportedError, - put: throwModifiesNotSupportedError, - post: throwModifiesNotSupportedError, - delete: throwModifiesNotSupportedError + extends: { + get: throwExtendsNotSupportedError, + put: throwExtendsNotSupportedError, + post: throwExtendsNotSupportedError, + delete: throwExtendsNotSupportedError } }; }; @@ -104,7 +104,7 @@ function FluentInterface(server, o) { post: fluentCreate('post', route), delete: fluentCreate('delete', route) }, - modifies: { + extends: { get: fluentModify('get', route), put: fluentModify('put', route), post: fluentModify('post', route), diff --git a/lib/route.js b/lib/route.js index f642e39..6ae4b70 100644 --- a/lib/route.js +++ b/lib/route.js @@ -64,7 +64,7 @@ Route.prototype.setResponseBody = function (body) { Route.prototype.mergeResponseBody = function (body) { this.debug('Merging', body, 'into', this.response.body); - this.response.body = deepmerge(this.response.body, body); + this.response.body = deepmerge(this.response.body, body, { alwaysPush: true }); this.debug('Response body is now', this.response.body); }; diff --git a/package.json b/package.json index e14756f..128d470 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ "connect-json": "0.0.0", "body-parser": "~1.0.2", "pegjs": "^0.8.0", - "deepmerge": "^0.2.7" + "deepmerge": "git://github.com/basicallydan/deepmerge.git#12f172671b56449522d5c1b54a8517ffeaab61e7" }, "license": "MIT", "engines": { diff --git a/readme.md b/readme.md index b7a8094..713b1f9 100644 --- a/readme.md +++ b/readme.md @@ -67,7 +67,7 @@ curl http://localhost:3000/next-items -X POST */ ``` -And you can specify endpoints which should only be created once other ones have been hit (conditional endpoints): +You can specify endpoints which should only be **created** once other ones have been hit. ```js var Interfake = require('interfake'); @@ -87,6 +87,39 @@ curl http://localhost:3000/next-items -X POST } +# Request: +curl http://localhost:3000/items/1 -X GET +# Response: +200 +{ + "id":1 + "name":"Item 1" +} +*/ +``` + +You can even specify how endpoints should be **modified** once others have been hit. + +```js +var Interfake = require('interfake'); +var interfake = new Interfake(); +interfake.get('/next-items').status(200).body({ items: [ { id: 1, name: 'Item 1' } ] }); +interfake.get('/items/2').status(200).body({ id: 1, name: 'Item 1' }); +var postResponse = interfake.post('/next-items').status(201).body({ created : true }); +postResponse.creates.get('/items/2').status(200).body({ id: 2, name: 'Item 2' }); +postResponse.extends.get('/next-items').status(200).body({ items: [ { id: 2, name: 'Item 2' } ] }); +interfake.listen(3000); + +/* +# Request: +curl http://localhost:3000/next-items -X POST +# Response: +201 +{ + "created":true +} + + # Request: curl http://localhost:3000/items/1 -X GET # Response: @@ -167,6 +200,7 @@ interfake.listen(3000); // The server will listen on port 3000 * Also accepts a delay range in the format 'ms..ms' e.g. '50..100' * `#responseHeaders(headers)`: An object containing response headers. The keys are header names. * `#creates#get|post|put|delete(url)`: Specify an endpoint to create *after* the first execution of this one. API is the same as above. + * `#extends#get|post|put|delete(url)`: Specify an endpoint to modify *after* the first execution of this one. API is the same as above. The URLs that you modify are matched based on `url` and `query`. The `status`, `body`, `delay` and `responseHeaders` are the modifiable bits. ## Method 2: Command line diff --git a/tests/fluent.test.js b/tests/fluent.test.js index 30f5ed9..2acaa8a 100644 --- a/tests/fluent.test.js +++ b/tests/fluent.test.js @@ -464,7 +464,7 @@ describe('Interfake Fluent JavaScript API', function () { tookTooLong = true; }, 55); - request.post({ url : 'http://localhost:3000/fluent', json : true }, function (error, response, body) { + request.post({ url : 'http://localhost:3000/fluent', json : true }, function () { clearTimeout(timeout); if(!enoughTimeHasPassed) { throw new Error('Response wasn\'t delay for long enough'); @@ -695,12 +695,12 @@ describe('Interfake Fluent JavaScript API', function () { }); }); - // Testing #modifies stuff - describe('#modifies', function () { + // Testing #extends stuff + describe('#extends', function () { describe('#get()', function() { describe('#body()', function () { - it('should create a GET endpoint which modifies its own body when it gets called', function (done) { - interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).modifies.get('/fluent').body({ what: 'ever' }); + it('should create a GET endpoint which extends its own body when it gets called', function (done) { + interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).extends.get('/fluent').body({ what: 'ever' }); interfake.listen(3000); get({url:'http://localhost:3000/fluent',json:true}) @@ -720,11 +720,62 @@ describe('Interfake Fluent JavaScript API', function () { }) .done(); }); + + it('should create a POST endpoint which adds to a GET endpoint with deep array when it gets called', function (done) { + interfake.get('/items').body({ items : [ { id: 1 } ] }); + interfake.post('/items').status(201).extends.get('/items').body({ items : [ { id : 2 } ] }); + interfake.listen(3000); + + get({url:'http://localhost:3000/items',json:true}) + .then(function (results) { + assert.equal(results[0].statusCode, 200); + assert.equal(results[1].items[0].id, 1); + assert.equal(results[1].items.length, 1); + return post({url:'http://localhost:3000/items',json:true}); + }) + .then(function (results) { + assert.equal(results[0].statusCode, 201); + return get({url:'http://localhost:3000/items',json:true}); + }) + .then(function (results) { + assert.equal(results[0].statusCode, 200); + assert.equal(results[1].items[0].id, 1); + assert.equal(results[1].items[1].id, 2); + assert.equal(results[1].items.length, 2); + done(); + }) + .done(); + }); + + it('should create a POST endpoint which adds to a GET endpoint with top-level array when it gets called', function (done) { + interfake.get('/items').body([ { id: 1 } ]); + interfake.post('/items').status(201).extends.get('/items').body([ { id : 2 } ]); + interfake.listen(3000); + + get({url:'http://localhost:3000/items',json:true}) + .then(function (results) { + assert.equal(results[0].statusCode, 200); + assert.equal(results[1][0].id, 1); + assert.equal(results[1][1], undefined); + return post({url:'http://localhost:3000/items',json:true}); + }) + .then(function (results) { + assert.equal(results[0].statusCode, 201); + return get({url:'http://localhost:3000/items',json:true}); + }) + .then(function (results) { + assert.equal(results[0].statusCode, 200); + assert.equal(results[1][0].id, 1); + assert.equal(results[1][1].id, 2); + done(); + }) + .done(); + }); }); describe('#status()', function () { - it('should create a GET endpoint which modifies its own status when it gets called', function (done) { - interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).modifies.get('/fluent').status(401); + it('should create a GET endpoint which extends its own status when it gets called', function (done) { + interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).extends.get('/fluent').status(401); interfake.listen(3000); get({url:'http://localhost:3000/fluent',json:true}) @@ -747,8 +798,8 @@ describe('Interfake Fluent JavaScript API', function () { }); describe('#responseHeaders()', function () { - it('should create a GET endpoint which modifies its own response headers when it gets called', function (done) { - interfake.get('/fluent').responseHeaders({ 'Awesome-Header' : 'Awesome Value' }).modifies.get('/fluent').responseHeaders({ 'Lame-Header' : 'Lame Value' }); + it('should create a GET endpoint which extends its own response headers when it gets called', function (done) { + interfake.get('/fluent').responseHeaders({ 'Awesome-Header' : 'Awesome Value' }).extends.get('/fluent').responseHeaders({ 'Lame-Header' : 'Lame Value' }); interfake.listen(3000); get({url:'http://localhost:3000/fluent',json:true}) @@ -769,7 +820,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#delay()', function () { it('should create a GET endpoint which adds a delay to itself when it gets called', function (done) { var enoughTimeHasPassed; - interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).modifies.get('/fluent').delay(50); + interfake.get('/fluent').body({ hello : 'there', goodbye: 'for now' }).extends.get('/fluent').delay(50); interfake.listen(3000); setTimeout(function() { @@ -799,7 +850,7 @@ describe('Interfake Fluent JavaScript API', function () { it('should create a GET endpoint which adds a delay to a different endpoint when it gets called', function (done) { var enoughTimeHasPassed, tookTooLong; - interfake.get('/fluent').modifies.get('/needs-delay').delay(50); + interfake.get('/fluent').extends.get('/needs-delay').delay(50); interfake.get('/needs-delay'); interfake.listen(3000); @@ -834,8 +885,8 @@ describe('Interfake Fluent JavaScript API', function () { }); describe('#query()', function () { - it('should create a GET endpoint with a query which modifies its own status', function (done) { - interfake.get('/fluent').query({ page : 2 }).status(200).modifies.get('/fluent').query({ page : 2 }).status(300); + it('should create a GET endpoint with a query which extends its own status', function (done) { + interfake.get('/fluent').query({ page : 2 }).status(200).extends.get('/fluent').query({ page : 2 }).status(300); interfake.listen(3000); get('http://localhost:3000/fluent?page=2') @@ -855,7 +906,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#get', function () { it('should produce a useful error when trying to spawn a GET endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').creates.get('/error'); + interfake.get('/fluent').extends.get('/fluent').creates.get('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet create new routes after their response. This is planned for a future version of Interfake.'); done(); @@ -866,7 +917,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#post', function () { it('should produce a useful error when trying to spawn a POST endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').creates.post('/error'); + interfake.get('/fluent').extends.get('/fluent').creates.post('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet create new routes after their response. This is planned for a future version of Interfake.'); done(); @@ -877,7 +928,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#put', function () { it('should produce a useful error when trying to spawn a PUT endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').creates.put('/error'); + interfake.get('/fluent').extends.get('/fluent').creates.put('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet create new routes after their response. This is planned for a future version of Interfake.'); done(); @@ -888,7 +939,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#delete', function () { it('should produce a useful error when trying to spawn a DELETE endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').creates.delete('/error'); + interfake.get('/fluent').extends.get('/fluent').creates.delete('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet create new routes after their response. This is planned for a future version of Interfake.'); done(); @@ -898,11 +949,11 @@ describe('Interfake Fluent JavaScript API', function () { }); }); - describe('#modifies', function () { + describe('#extends', function () { describe('#get', function () { it('should produce a useful error when trying to modify an existing GET endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').modifies.get('/error'); + interfake.get('/fluent').extends.get('/fluent').extends.get('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet modify existing routes after their response. This is planned for a future version of Interfake.'); done(); @@ -913,7 +964,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#post', function () { it('should produce a useful error when trying to modify an existing POST endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').modifies.post('/error'); + interfake.get('/fluent').extends.get('/fluent').extends.post('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet modify existing routes after their response. This is planned for a future version of Interfake.'); done(); @@ -924,7 +975,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#put', function () { it('should produce a useful error when trying to modify an existing PUT endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').modifies.put('/error'); + interfake.get('/fluent').extends.get('/fluent').extends.put('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet modify existing routes after their response. This is planned for a future version of Interfake.'); done(); @@ -935,7 +986,7 @@ describe('Interfake Fluent JavaScript API', function () { describe('#delete', function () { it('should produce a useful error when trying to modify an existing DELETE endpoint from a modified endpoint', function (done) { assert.throws(function () { - interfake.get('/fluent').modifies.get('/fluent').modifies.delete('/error'); + interfake.get('/fluent').extends.get('/fluent').extends.delete('/error'); }, function (err) { assert.equal(err.message, 'Sorry, but modified routes cannot yet modify existing routes after their response. This is planned for a future version of Interfake.'); done(); @@ -948,8 +999,8 @@ describe('Interfake Fluent JavaScript API', function () { describe('#put()', function () { describe('#body()', function () { - it('should create a PUT endpoint which modifies its own body when it gets called', function (done) { - interfake.put('/fluent').body({ version : 1 }).modifies.put('/fluent').body({ version: 2 }); + it('should create a PUT endpoint which extends its own body when it gets called', function (done) { + interfake.put('/fluent').body({ version : 1 }).extends.put('/fluent').body({ version: 2 }); interfake.listen(3000); put({url:'http://localhost:3000/fluent',json:true}) @@ -970,8 +1021,8 @@ describe('Interfake Fluent JavaScript API', function () { describe('#post()', function () { describe('#body()', function () { - it('should create a POST endpoint which modifies its own body when it gets called', function (done) { - interfake.post('/fluent').body({ version : 1 }).modifies.post('/fluent').body({ version: 2 }); + it('should create a POST endpoint which extends its own body when it gets called', function (done) { + interfake.post('/fluent').body({ version : 1 }).extends.post('/fluent').body({ version: 2 }); interfake.listen(3000); post({url:'http://localhost:3000/fluent',json:true}) @@ -992,8 +1043,8 @@ describe('Interfake Fluent JavaScript API', function () { describe('#delete()', function () { describe('#body()', function () { - it('should create a DELETE endpoint which modifies its own body when it gets called', function (done) { - interfake.delete('/fluent').body({ version : 1 }).modifies.delete('/fluent').body({ version: 2 }); + it('should create a DELETE endpoint which extends its own body when it gets called', function (done) { + interfake.delete('/fluent').body({ version : 1 }).extends.delete('/fluent').body({ version: 2 }); interfake.listen(3000); del({url:'http://localhost:3000/fluent',json:true})