From dfcb04fd090cb712c31c5b888fd123126124a131 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Wed, 11 Jan 2017 13:41:07 -0500 Subject: [PATCH 1/9] Setting up search tests --- client/admin/adminSearch/adminSearch.jsx | 2 +- test/search.test.js | 69 ++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/client/admin/adminSearch/adminSearch.jsx b/client/admin/adminSearch/adminSearch.jsx index 1f95153..15d0972 100644 --- a/client/admin/adminSearch/adminSearch.jsx +++ b/client/admin/adminSearch/adminSearch.jsx @@ -11,7 +11,7 @@ const AdminSearch = React.createClass({ }, render: function(){ return
- AdminSearch Component Ready. +

Admin Search

} }); diff --git a/test/search.test.js b/test/search.test.js index e69de29..c4ab4e0 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -0,0 +1,69 @@ +const testing = require('./test.init.js'); +const _ = require('lodash'); + +const DB = require('db.js'); +const BrewData = require('brew.data.js'); +const Error = require('error.js'); + +const ids = (brewIds) => { + return _.map(brewIds, (brewId) => { + return { editId : brews[brewId].editId }; + }); +} + +const brews = { + BrewA : { + title : 'BrewA', + description : 'fancy', + authors : [], + systems : [] + }, + BrewB : { + title : 'BrewB', + description : 'fancy', + authors : [], + systems : [] + }, + BrewC : { + title : 'BrewC', + description : 'test', + authors : [], + systems : [] + }, + BrewD : { + title : 'BrewD', + description : 'test', + authors : [], + systems : [] + } +}; + +describe('Brew Search', () => { + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return Promise.all(_.map(brews, (brewData, id) => { + return BrewData.create(brewData) + .then((brew)=>{ brews[id] = brew; }); + })); + }); + + it('should find brews based on title and/or description', () => { + + return new Promise((resolve, reject) => { + return reject() + }) + .catch(()=>{ console.log('here1');}) + .catch(()=>{ console.log('here2');}) + + return BrewData.create({ + text : 'Brew Text' + }).then((brew) => { + +}); + //result.count.should.be.equal(2) + //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + + +}); \ No newline at end of file From a405c7cfb21d303125f5318f830c36b87cf8d42e Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Wed, 11 Jan 2017 19:56:57 -0800 Subject: [PATCH 2/9] Stubbing out tests for searching --- server/brew.data.js | 10 ++++++- test/search.test.js | 66 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/server/brew.data.js b/server/brew.data.js index 367d33b..217c8cf 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -93,7 +93,15 @@ const BrewData = { return BrewData.get({ editId }); }, - search : (query, req={}) => { + search : (query, pagniation, sorting, permissions) => { + + + //search with query, add in `published = false` + // filter out editId and text + + //if admin, removed published=false, remove filtering editId + + //defaults with page and count //returns a non-text version of brews //assume sanatized ? diff --git a/test/search.test.js b/test/search.test.js index c4ab4e0..091f2df 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -16,25 +16,33 @@ const brews = { title : 'BrewA', description : 'fancy', authors : [], - systems : [] + systems : [], + views : 12, + published : true }, BrewB : { title : 'BrewB', description : 'fancy', authors : [], - systems : [] + systems : [], + views : 7, + published : true }, BrewC : { title : 'BrewC', description : 'test', authors : [], - systems : [] + systems : [], + views : 0, + published : false }, BrewD : { title : 'BrewD', description : 'test', authors : [], - systems : [] + systems : [], + views : 1, + published : true } }; @@ -48,21 +56,47 @@ describe('Brew Search', () => { })); }); - it('should find brews based on title and/or description', () => { - return new Promise((resolve, reject) => { - return reject() - }) - .catch(()=>{ console.log('here1');}) - .catch(()=>{ console.log('here2');}) + describe('Searching', ()=>{ + it('should be able to search for all brews', ()=>{ - return BrewData.create({ - text : 'Brew Text' - }).then((brew) => { -}); - //result.count.should.be.equal(2) - //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + it('should find brews based on title and/or description', () => { + + //result.count.should.be.equal(2) + //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + }); + + it('should return the total number of brews and page info for query', ()=>{ + + }); + }) + + describe('Permissions', () => { + it('should only fetch published brews', () => { + + }); + it('fetched brews should not have text or editId', () => { + + }); + it('if admin, fetches also non-published brews, with editid', () => { + + }); + it('if author, fetches also non-published brews, with editid', ()=>{ + + }); + }); + + describe('Pagniation', () => { + it('should return the exact number of brews based on limit', () => { + + }); + + }); + + desscribe('Sorting', ()=>{ + }); From efee8ff05cb7112b956f0ce74d3072215a1db015 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Mon, 23 Jan 2017 00:35:30 -0500 Subject: [PATCH 3/9] Basic search is working --- package.json | 1 + server/brew.data.js | 55 +++++++++++++++++++++++++++++------ test/search.test.js | 71 +++++++++++++++++++++++++++++++++++---------- test/test.init.js | 6 +++- 4 files changed, 107 insertions(+), 26 deletions(-) diff --git a/package.json b/package.json index e553ba5..951b9a6 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "app-module-path": "^2.1.0", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", + "chai-subset": "^1.4.0", "mocha": "^3.2.0", "supertest": "^2.0.1", "supertest-as-promised": "^4.0.2" diff --git a/server/brew.data.js b/server/brew.data.js index 217c8cf..d8281e1 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -21,7 +21,8 @@ const BrewSchema = mongoose.Schema({ createdAt : { type: Date, default: Date.now }, updatedAt : { type: Date, default: Date.now}, lastViewed : { type: Date, default: Date.now}, - views : {type:Number, default:0} + views : {type:Number, default:0}, + version : {type: Number, default:1} }, { versionKey: false, toJSON : { @@ -32,6 +33,8 @@ const BrewSchema = mongoose.Schema({ } }); +BrewSchema.index({ title: "text", description: "text" }); + BrewSchema.methods.increaseView = function(){ this.views = this.views + 1; return this.save(); @@ -93,19 +96,53 @@ const BrewData = { return BrewData.get({ editId }); }, - search : (query, pagniation, sorting, permissions) => { + search : (searchTerms, pagination, sorting, fullAccess = true) => { + let query = {}; + if(searchTerms){ + query = {$text: { + //Wrap terms in quots to perform an AND operator + $search: _.map(searchTerms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + } + + pagination = _.defaults(pagination, { + limit : 25, + page : 0 + }); + sorting = _.defaults(sorting, { + 'views' : 1 + }); + let filter = { + //editId : 0, + text : 0 + }; - //search with query, add in `published = false` - // filter out editId and text + if(!fullAccess){ + filter.editId = 0; + query.published = false; + } - //if admin, removed published=false, remove filtering editId + const searchQuery = Brew + .find(query) + .sort(sorting) + .select(filter) + .limit(pagination.limit) + .skip(pagination.page * pagination.limit) + .exec(); + const countQuery = Brew.count(query).exec(); - //defaults with page and count - //returns a non-text version of brews - //assume sanatized ? - return Promise.resolve([]); + return Promise.all([searchQuery, countQuery]) + .then((result) => { + return { + brews : result[0], + total : result[1] + } + }); }, diff --git a/test/search.test.js b/test/search.test.js index 091f2df..56352dd 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -18,11 +18,11 @@ const brews = { authors : [], systems : [], views : 12, - published : true + published : false }, BrewB : { title : 'BrewB', - description : 'fancy', + description : 'very fancy', authors : [], systems : [], views : 7, @@ -38,7 +38,7 @@ const brews = { }, BrewD : { title : 'BrewD', - description : 'test', + description : 'test super amazing brew for 5e. Geared for Rangers.', authors : [], systems : [], views : 1, @@ -59,43 +59,82 @@ describe('Brew Search', () => { describe('Searching', ()=>{ it('should be able to search for all brews', ()=>{ - - + return BrewData.search() + .then((result) => { + result.total.should.be.equal(_.size(brews)); + result.brews.length.should.be.equal(_.size(brews)); + }) }); - it('should find brews based on title and/or description', () => { - - //result.count.should.be.equal(2) - //result.brews.should.deep.include.members(ids(['BrewA', 'BrewB']); + it('should search brews based on title', () => { + return BrewData.search('BrewC') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewC'])); + }) }); - it('should return the total number of brews and page info for query', ()=>{ + it('should search brews based on description', () => { + return BrewData.search('fancy') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + }) + }); + + it('should search brews based on multiple terms', () => { + return BrewData.search('ranger 5e') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewD'])); + }) + }); + + it('should perform an AND operation on the provided terms', () => { + return BrewData.search('BrewD GARBAGE') + .then((result) => { + result.total.should.be.equal(0); + }); + }); + + it('should search brews based on a combination of both', () => { + return BrewData.search('BrewB fancy') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewB'])); + }); + }); + + it.skip('should be able to search for a specific system', ()=>{ + + }); + it.skip('should be able to search for a specifc user', ()=>{ }); }) describe('Permissions', () => { - it('should only fetch published brews', () => { + it.skip('should only fetch published brews', () => { }); - it('fetched brews should not have text or editId', () => { + it.skip('fetched brews should not have text or editId', () => { }); - it('if admin, fetches also non-published brews, with editid', () => { + it.skip('if admin, fetches also non-published brews, with editid', () => { }); - it('if author, fetches also non-published brews, with editid', ()=>{ + it.skip('if author, fetches also non-published brews, with editid', ()=>{ }); }); describe('Pagniation', () => { - it('should return the exact number of brews based on limit', () => { + it.skip('should return the exact number of brews based on limit', () => { }); }); - desscribe('Sorting', ()=>{ + describe('Sorting', ()=>{ }); diff --git a/test/test.init.js b/test/test.init.js index 7dda4f8..2c8c8ec 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -7,7 +7,11 @@ const config = require('nconf') .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) .file('defaults', { file: 'config/default.json' }); -const should = require('chai').use(require('chai-as-promised')).should(); +const should = require('chai') + .use(require('chai-as-promised')) + .use(require('chai-subset')) + .should(); + const log = require('loglevel'); log.setLevel(config.get('log_level')); From 2f82d3875e9a1c3ba7edc7485bf8daf76e5a3862 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 09:56:44 -0500 Subject: [PATCH 4/9] Adding a new script from populating the DB with a bunch of random brews --- package.json | 1 + scripts/genbrews.js | 51 +++++++++++++++++++ server/brew.data.js | 12 +++-- server/db.js | 6 +-- .../metadataEditor/metadataEditor.less | 3 ++ 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 scripts/genbrews.js diff --git a/package.json b/package.json index 951b9a6..df28a42 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "quick": "node scripts/quick.js", "build": "node scripts/build.js", "phb": "node scripts/phb.js", + "genbrews": "node scripts/genbrews.js", "prod": "set NODE_ENV=production&& npm run build", "postinstall": "npm run build", "start": "node server.js", diff --git a/scripts/genbrews.js b/scripts/genbrews.js new file mode 100644 index 0000000..0005304 --- /dev/null +++ b/scripts/genbrews.js @@ -0,0 +1,51 @@ +//Populates the DB with a bunch of brews for UI testing + +const _ = require('lodash'); + +const DB = require('../server/db.js'); +const BrewData = require('../server/brew.data.js'); + + +//TODO: pull in snippets and randomly add them + +const genBrew = () => { + return { + title : 'BrewA', + description : '', + text : '', + authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), + systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), + views : _.random(0,1000), + published : !!_.random(0,1) + } +} + +const randomBrews = _.times(20, genBrew); + +const specificBrews = [ + { + text : 'Cool test', + authors : ['test'] + } +]; + + +return Promise.resolve() + .then(DB.connect) + .then(BrewData.removeAll) + .then(() => { + console.log('Adding random brews...'); + return Promise.all(_.map(randomBrews, (brew) => { + return BrewData.create(brew); + })); + }) + .then(() => { + console.log('Adding specific brews...'); + return Promise.all(_.map(specificBrews, (brew) => { + return BrewData.create(brew); + })); + }) + .then(() => { + console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); + }) + .catch(console.error); diff --git a/server/brew.data.js b/server/brew.data.js index d8281e1..b6a5a85 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -22,7 +22,7 @@ const BrewSchema = mongoose.Schema({ updatedAt : { type: Date, default: Date.now}, lastViewed : { type: Date, default: Date.now}, views : {type:Number, default:0}, - version : {type: Number, default:1} + version : {type: Number, default:1} }, { versionKey: false, toJSON : { @@ -33,6 +33,7 @@ const BrewSchema = mongoose.Schema({ } }); +//Index these fields for fast text searching BrewSchema.index({ title: "text", description: "text" }); BrewSchema.methods.increaseView = function(){ @@ -42,9 +43,6 @@ BrewSchema.methods.increaseView = function(){ const Brew = mongoose.model('Brew', BrewSchema); - - - const BrewData = { schema : BrewSchema, model : Brew, @@ -96,6 +94,12 @@ const BrewData = { return BrewData.get({ editId }); }, + + //TODO: Add a 'core search' which just takes a search object + //TODO: extend the core search with a user search and a term search + //TODO: break these functions off into a 'brew.search.js' file + //TODO: pagniation, sorting and full access should be an 'opts' param + search : (searchTerms, pagination, sorting, fullAccess = true) => { let query = {}; if(searchTerms){ diff --git a/server/db.js b/server/db.js index a9c5400..6cb0986 100644 --- a/server/db.js +++ b/server/db.js @@ -8,14 +8,14 @@ module.exports = { connect : ()=>{ return new Promise((resolve, reject)=>{ if(mongoose.connection.readyState == 1){ - log.info('DB already connected'); + log.warn('DB already connected'); return resolve(); } mongoose.connect(dbPath, (err) => { if(err){ - log.info('Error : Could not connect to a Mongo Database.'); - log.info(' If you are running locally, make sure mongodb.exe is running.'); + log.error('Error : Could not connect to a Mongo Database.'); + log.error(' If you are running locally, make sure mongodb.exe is running.'); return reject(err); } log.info('DB connected.'); diff --git a/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less b/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less index c3141f0..2f9812d 100644 --- a/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less +++ b/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less @@ -76,4 +76,7 @@ font-size: 0.8em; line-height : 1.5em; } + .thumbnail.field{ + + } } \ No newline at end of file From 8e58e5aca92fe931e42d217c28c23c3a9ad2d34d Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 10:36:07 -0500 Subject: [PATCH 5/9] MOved the brews search to its own file, writing out more tests --- scripts/genbrews.js | 1 + server/brew.data.js | 61 +------------------ server/brew.search.js | 59 ++++++++++++++++++ server/db.js | 8 +++ test/api.test.js | 24 ++++++++ test/search.test.js | 136 ++++++++++++++++++++++++++---------------- 6 files changed, 180 insertions(+), 109 deletions(-) create mode 100644 server/brew.search.js diff --git a/scripts/genbrews.js b/scripts/genbrews.js index 0005304..e6dac10 100644 --- a/scripts/genbrews.js +++ b/scripts/genbrews.js @@ -47,5 +47,6 @@ return Promise.resolve() }) .then(() => { console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); + return DB.close(); }) .catch(console.error); diff --git a/server/brew.data.js b/server/brew.data.js index b6a5a85..c6e92ee 100644 --- a/server/brew.data.js +++ b/server/brew.data.js @@ -93,63 +93,8 @@ const BrewData = { getByEdit : (editId) => { return BrewData.get({ editId }); }, - - - //TODO: Add a 'core search' which just takes a search object - //TODO: extend the core search with a user search and a term search - //TODO: break these functions off into a 'brew.search.js' file - //TODO: pagniation, sorting and full access should be an 'opts' param - - search : (searchTerms, pagination, sorting, fullAccess = true) => { - let query = {}; - if(searchTerms){ - query = {$text: { - //Wrap terms in quots to perform an AND operator - $search: _.map(searchTerms.split(' '), (term)=>{ - return `\"${term}\"`; - }).join(' '), - $caseSensitive : false - }}; - } - - pagination = _.defaults(pagination, { - limit : 25, - page : 0 - }); - sorting = _.defaults(sorting, { - 'views' : 1 - }); - let filter = { - //editId : 0, - text : 0 - }; - - - if(!fullAccess){ - filter.editId = 0; - query.published = false; - } - - const searchQuery = Brew - .find(query) - .sort(sorting) - .select(filter) - .limit(pagination.limit) - .skip(pagination.page * pagination.limit) - .exec(); - - const countQuery = Brew.count(query).exec(); - - return Promise.all([searchQuery, countQuery]) - .then((result) => { - return { - brews : result[0], - total : result[1] - } - }); - }, - - }; -module.exports = BrewData; \ No newline at end of file +const BrewSearch = require('./brew.search.js')(Brew); + +module.exports = _.merge(BrewData, BrewSearch); \ No newline at end of file diff --git a/server/brew.search.js b/server/brew.search.js new file mode 100644 index 0000000..99e993e --- /dev/null +++ b/server/brew.search.js @@ -0,0 +1,59 @@ +const _ = require('lodash'); + +module.exports = (Brew) => { + const cmds = { + termSearch : (terms='', opts, fullAccess) => { + const query = {$text: { + //Wrap terms in quotes to perform an AND operation + $search: _.map(terms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + return cmds.search(query, opts, fullAccess); + }, + + userSearch : (username, opts, fullAccess) => { + const query = { + authors : username + }; + + return cmds.search(query, opts, fullAccess); + }, + + search : (queryObj={}, opts={}, fullAccess = true) => { + const pagination = _.defaults(opts.pagination, { + limit : 25, + page : 0 + }); + const sorting = _.defaults(opts.sorting, { + 'views' : 1 + }); + let filter = { + text : 0 + }; + if(!fullAccess){ + filter.editId = 0; + queryObj.published = false; + } + + const searchQuery = Brew + .find(queryObj) + .sort(sorting) + .select(filter) + .limit(pagination.limit) + .skip(pagination.page * pagination.limit) + .exec(); + const countQuery = Brew.count(queryObj).exec(); + + return Promise.all([searchQuery, countQuery]) + .then((result) => { + return { + brews : result[0], + total : result[1] + } + }); + } + }; + return cmds; +}; \ No newline at end of file diff --git a/server/db.js b/server/db.js index 6cb0986..d0756bf 100644 --- a/server/db.js +++ b/server/db.js @@ -24,5 +24,13 @@ module.exports = { ); }); }, + close : ()=>{ + return new Promise((resolve, reject) => { + mongoose.connection.close(()=>{ + log.info('DB connection closed.'); + return resolve(); + }); + }); + }, instance : mongoose } \ No newline at end of file diff --git a/test/api.test.js b/test/api.test.js index 822683c..31330e5 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -111,4 +111,28 @@ describe('Brew API', () => { }); }); + describe('Search', () => { + it.skip('should be able to search for brews with given terms', ()=>{ + + }); + it.skip('should exclude unpublished brews and have no editIdsh', ()=>{ + + }); + it.skip('should sort the search', ()=>{ + + }); + it.skip('should use pagniation on the search', ()=>{ + + }); + }); + + describe('User', () => { + it.skip('should be able to query brews for a specific user', ()=>{ + + }); + it.skip('should return full access to brews if loggedin user is queried user', ()=>{ + + }); + }); + }); \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 56352dd..5efdd1f 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -11,11 +11,13 @@ const ids = (brewIds) => { }); } + +//TODO: Move this brew generator to test.init const brews = { BrewA : { title : 'BrewA', description : 'fancy', - authors : [], + authors : ['userA'], systems : [], views : 12, published : false @@ -23,7 +25,7 @@ const brews = { BrewB : { title : 'BrewB', description : 'very fancy', - authors : [], + authors : ['userA'], systems : [], views : 7, published : true @@ -31,7 +33,7 @@ const brews = { BrewC : { title : 'BrewC', description : 'test', - authors : [], + authors : ['userA', 'userB'], systems : [], views : 0, published : false @@ -39,7 +41,7 @@ const brews = { BrewD : { title : 'BrewD', description : 'test super amazing brew for 5e. Geared for Rangers.', - authors : [], + authors : ['userC'], systems : [], views : 1, published : true @@ -58,6 +60,10 @@ describe('Brew Search', () => { describe('Searching', ()=>{ + it.skip('should return a total and a brew array', ()=>{ + + }); + it('should be able to search for all brews', ()=>{ return BrewData.search() .then((result) => { @@ -65,52 +71,34 @@ describe('Brew Search', () => { result.brews.length.should.be.equal(_.size(brews)); }) }); - it('should search brews based on title', () => { - return BrewData.search('BrewC') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewC'])); - }) - }); + }); - it('should search brews based on description', () => { - return BrewData.search('fancy') - .then((result) => { - result.total.should.be.equal(2); - result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); - }) - }); - - it('should search brews based on multiple terms', () => { - return BrewData.search('ranger 5e') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewD'])); - }) - }); - - it('should perform an AND operation on the provided terms', () => { - return BrewData.search('BrewD GARBAGE') - .then((result) => { - result.total.should.be.equal(0); - }); - }); - - it('should search brews based on a combination of both', () => { - return BrewData.search('BrewB fancy') - .then((result) => { - result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewB'])); - }); - }); - - it.skip('should be able to search for a specific system', ()=>{ + describe('Pagniation', () => { + it.skip('should return the exact number of brews based on limit', () => { }); - it.skip('should be able to search for a specifc user', ()=>{ + + it.skip('should return the correct pages when specified', () => { }); - }) + + it.skip('should return a partial list if on the last page', () => { + + }); + + }); + + describe('Sorting', ()=>{ + it.skip('should sort ASC', () => { + + }); + it.skip('should sort DESC', () => { + + }); + it.skip('should sort based on multiple fields', () => { + + }); + }); describe('Permissions', () => { it.skip('should only fetch published brews', () => { @@ -127,16 +115,62 @@ describe('Brew Search', () => { }); }); - describe('Pagniation', () => { - it.skip('should return the exact number of brews based on limit', () => { + + describe('Term Search', ()=>{ + it('should search brews based on title', () => { + return BrewData.termSearch('BrewC') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewC'])); + }) }); + it('should search brews based on description', () => { + return BrewData.termSearch('fancy') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + }) + }); + + it('should search brews based on multiple terms', () => { + return BrewData.termSearch('ranger 5e') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewD'])); + }) + }); + + it('should perform an AND operation on the provided terms', () => { + return BrewData.termSearch('BrewD GARBAGE') + .then((result) => { + result.total.should.be.equal(0); + }); + }); + + it('should search brews based on a combination of both', () => { + return BrewData.termSearch('BrewB fancy') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.containSubset(ids(['BrewB'])); + }); + }); }); - describe('Sorting', ()=>{ - + describe('User Search', ()=>{ + it('should return brews just for a single user', () => { + return BrewData.userSearch('userA') + .then((result) => { + result.total.should.be.equal(3); + result.brews.should.containSubset(ids(['BrewA', 'BrewB', 'BrewC'])); + }); + }); + it('should return nothing if provided a non-exsistent user', () => { + return BrewData.userSearch('userXYZ') + .then((result) => { + result.total.should.be.equal(0); + }); + }); }); - - }); \ No newline at end of file From 8018442f25e158b12c7a83c01b8e6427f404b53b Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 10:47:38 -0500 Subject: [PATCH 6/9] Upgrading the brew generation for testing --- test/search.test.js | 19 +++++++++++-------- test/test.init.js | 11 +++++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/test/search.test.js b/test/search.test.js index 5efdd1f..4d88145 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -1,4 +1,4 @@ -const testing = require('./test.init.js'); +const Test = require('./test.init.js'); const _ = require('lodash'); const DB = require('db.js'); @@ -15,7 +15,7 @@ const ids = (brewIds) => { //TODO: Move this brew generator to test.init const brews = { BrewA : { - title : 'BrewA', + title : 'Brew-Alpha', description : 'fancy', authors : ['userA'], systems : [], @@ -23,7 +23,7 @@ const brews = { published : false }, BrewB : { - title : 'BrewB', + title : 'Brew-Beta', description : 'very fancy', authors : ['userA'], systems : [], @@ -31,7 +31,7 @@ const brews = { published : true }, BrewC : { - title : 'BrewC', + title : 'Brew-Charlie', description : 'test', authors : ['userA', 'userB'], systems : [], @@ -39,7 +39,7 @@ const brews = { published : false }, BrewD : { - title : 'BrewD', + title : 'Brew-Delta', description : 'test super amazing brew for 5e. Geared for Rangers.', authors : ['userC'], systems : [], @@ -119,7 +119,7 @@ describe('Brew Search', () => { describe('Term Search', ()=>{ it('should search brews based on title', () => { - return BrewData.termSearch('BrewC') + return BrewData.termSearch('Charlie') .then((result) => { result.total.should.be.equal(1); result.brews.should.containSubset(ids(['BrewC'])); @@ -143,19 +143,22 @@ describe('Brew Search', () => { }); it('should perform an AND operation on the provided terms', () => { - return BrewData.termSearch('BrewD GARBAGE') + return BrewData.termSearch('Brew Delta GARBAGE') .then((result) => { result.total.should.be.equal(0); }); }); it('should search brews based on a combination of both', () => { - return BrewData.termSearch('BrewB fancy') + return BrewData.termSearch('Brew Beta fancy') .then((result) => { result.total.should.be.equal(1); result.brews.should.containSubset(ids(['BrewB'])); }); }); + it.skip('should not worry about the case of the terms', () => { + + }); }); describe('User Search', ()=>{ diff --git a/test/test.init.js b/test/test.init.js index 2c8c8ec..317df50 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -7,14 +7,17 @@ const config = require('nconf') .file('environment', { file: `config/${process.env.NODE_ENV}.json` }) .file('defaults', { file: 'config/default.json' }); -const should = require('chai') +const Chai = require('chai') .use(require('chai-as-promised')) - .use(require('chai-subset')) - .should(); + .use(require('chai-subset')); const log = require('loglevel'); log.setLevel(config.get('log_level')); +//TODO: extend should to have a brewCheck +// eg. result.brews.should.haveBrews('BrewA', 'BrewB') +// Then can remove chai-subset + module.exports = { - should: should + should: Chai.should() }; From a826aaffd95a157008ad7b84b327253107122315 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 18:38:09 -0500 Subject: [PATCH 7/9] created a brew generator and chai plugin for easier testing --- package.json | 2 +- scripts/genbrews.js | 52 --------------------------- scripts/populate.js | 22 ++++++++++++ test/brew.gen.js | 88 +++++++++++++++++++++++++++++++++++++++++++++ test/search.test.js | 64 ++++++--------------------------- test/test.init.js | 7 ++-- 6 files changed, 124 insertions(+), 111 deletions(-) delete mode 100644 scripts/genbrews.js create mode 100644 scripts/populate.js create mode 100644 test/brew.gen.js diff --git a/package.json b/package.json index df28a42..02d5ab8 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "quick": "node scripts/quick.js", "build": "node scripts/build.js", "phb": "node scripts/phb.js", - "genbrews": "node scripts/genbrews.js", + "populate": "node scripts/populate.js", "prod": "set NODE_ENV=production&& npm run build", "postinstall": "npm run build", "start": "node server.js", diff --git a/scripts/genbrews.js b/scripts/genbrews.js deleted file mode 100644 index e6dac10..0000000 --- a/scripts/genbrews.js +++ /dev/null @@ -1,52 +0,0 @@ -//Populates the DB with a bunch of brews for UI testing - -const _ = require('lodash'); - -const DB = require('../server/db.js'); -const BrewData = require('../server/brew.data.js'); - - -//TODO: pull in snippets and randomly add them - -const genBrew = () => { - return { - title : 'BrewA', - description : '', - text : '', - authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), - systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), - views : _.random(0,1000), - published : !!_.random(0,1) - } -} - -const randomBrews = _.times(20, genBrew); - -const specificBrews = [ - { - text : 'Cool test', - authors : ['test'] - } -]; - - -return Promise.resolve() - .then(DB.connect) - .then(BrewData.removeAll) - .then(() => { - console.log('Adding random brews...'); - return Promise.all(_.map(randomBrews, (brew) => { - return BrewData.create(brew); - })); - }) - .then(() => { - console.log('Adding specific brews...'); - return Promise.all(_.map(specificBrews, (brew) => { - return BrewData.create(brew); - })); - }) - .then(() => { - console.log(`\n Added ${randomBrews.length + specificBrews.length} brews.`); - return DB.close(); - }) - .catch(console.error); diff --git a/scripts/populate.js b/scripts/populate.js new file mode 100644 index 0000000..7a392af --- /dev/null +++ b/scripts/populate.js @@ -0,0 +1,22 @@ +//Populates the DB with a bunch of brews for UI testing +const _ = require('lodash'); + +const DB = require('../server/db.js'); +const BrewData = require('../server/brew.data.js'); +const BrewGen = require('../test/brew.gen.js'); + +return Promise.resolve() + .then(DB.connect) + .then(BrewData.removeAll) + .then(() => { + console.log('Adding random brews...'); + return return BrewGen.populateDB(BrewGen.random(5)); + }) + .then(() => { + console.log('Adding specific brews...'); + return return BrewGen.populateDB(BrewGen.static()); + }) + .then(() => { + return DB.close(); + }) + .catch(console.error); diff --git a/test/brew.gen.js b/test/brew.gen.js new file mode 100644 index 0000000..fa67643 --- /dev/null +++ b/test/brew.gen.js @@ -0,0 +1,88 @@ +const _ = require('lodash'); +const BrewData = require('../server/brew.data.js'); + +let PopulatedBrews = {}; + +module.exports = { + random : (num = 20)=>{ + return _.times(num, ()=>{ + //TODO: Build better generator + return { + title : 'BrewA', + description : '', + text : '', + authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)), + systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)), + views : _.random(0,1000), + published : !!_.random(0,1) + }; + }); + }, + static : () => { + return { + BrewA : { + title : 'Brew-Alpha', + description : 'fancy', + authors : ['userA'], + systems : [], + views : 12, + published : false + }, + BrewB : { + title : 'Brew-Beta', + description : 'very fancy', + authors : ['userA'], + systems : [], + views : 7, + published : true + }, + BrewC : { + title : 'Brew-Charlie', + description : 'test', + authors : ['userA', 'userB'], + systems : [], + views : 0, + published : false + }, + BrewD : { + title : 'Brew-Delta', + description : 'test super amazing brew for 5e. Geared for Rangers.', + authors : ['userC'], + systems : [], + views : 1, + published : true + } + }; + }, + + populateDB : (brewCollection)=>{ + return Promise.all(_.map(brewCollection, (brewData, id) => { + return BrewData.create(brewData) + .then((brew)=>{ + PopulatedBrews[id] = brew; + }); + }) + ); + }, + + chaiPlugin : (chai, utils) => { + chai.Assertion.addMethod('brews', function(...brewIds){ + new chai.Assertion(this._obj).to.be.instanceof(Array); + const valid = _.every(brewIds, (brewId) => { + const storedBrew = PopulatedBrews[brewId]; + if(!storedBrew) return false; + return _.some(this._obj, (brew)=>{ + return brew.editId == storedBrew.editId && + brew.shareId == storedBrew.shareId && + brew.title == storedBrew.title && + brew.views == storedBrew.views; + }); + }); + this.assert( + valid, + `expect #{this} to have brews ${brewIds.join(', ')}`, + `expect #{this} to not have brews ${brewIds.join(', ')}` + ) + }); + } +}; \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 4d88145..058199e 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -3,59 +3,16 @@ const _ = require('lodash'); const DB = require('db.js'); const BrewData = require('brew.data.js'); -const Error = require('error.js'); - -const ids = (brewIds) => { - return _.map(brewIds, (brewId) => { - return { editId : brews[brewId].editId }; - }); -} +const BrewGen = require('./brew.gen.js'); +//const Error = require('error.js'); -//TODO: Move this brew generator to test.init -const brews = { - BrewA : { - title : 'Brew-Alpha', - description : 'fancy', - authors : ['userA'], - systems : [], - views : 12, - published : false - }, - BrewB : { - title : 'Brew-Beta', - description : 'very fancy', - authors : ['userA'], - systems : [], - views : 7, - published : true - }, - BrewC : { - title : 'Brew-Charlie', - description : 'test', - authors : ['userA', 'userB'], - systems : [], - views : 0, - published : false - }, - BrewD : { - title : 'Brew-Delta', - description : 'test super amazing brew for 5e. Geared for Rangers.', - authors : ['userC'], - systems : [], - views : 1, - published : true - } -}; describe('Brew Search', () => { before('Connect DB', DB.connect); before('Clear DB', BrewData.removeAll); before('Populate brews', ()=>{ - return Promise.all(_.map(brews, (brewData, id) => { - return BrewData.create(brewData) - .then((brew)=>{ brews[id] = brew; }); - })); + return BrewGen.populateDB(BrewGen.static()); }); @@ -67,8 +24,9 @@ describe('Brew Search', () => { it('should be able to search for all brews', ()=>{ return BrewData.search() .then((result) => { - result.total.should.be.equal(_.size(brews)); - result.brews.length.should.be.equal(_.size(brews)); + const brewCount = _.size(BrewGen.static()); + result.total.should.be.equal(brewCount); + result.brews.length.should.be.equal(brewCount); }) }); }); @@ -122,7 +80,7 @@ describe('Brew Search', () => { return BrewData.termSearch('Charlie') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewC'])); + result.brews.should.have.brews('BrewC'); }) }); @@ -130,7 +88,7 @@ describe('Brew Search', () => { return BrewData.termSearch('fancy') .then((result) => { result.total.should.be.equal(2); - result.brews.should.containSubset(ids(['BrewA', 'BrewB'])); + result.brews.should.have.brews('BrewA', 'BrewB'); }) }); @@ -138,7 +96,7 @@ describe('Brew Search', () => { return BrewData.termSearch('ranger 5e') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewD'])); + result.brews.should.have.brews('BrewD'); }) }); @@ -153,7 +111,7 @@ describe('Brew Search', () => { return BrewData.termSearch('Brew Beta fancy') .then((result) => { result.total.should.be.equal(1); - result.brews.should.containSubset(ids(['BrewB'])); + result.brews.should.have.brews('BrewB'); }); }); it.skip('should not worry about the case of the terms', () => { @@ -166,7 +124,7 @@ describe('Brew Search', () => { return BrewData.userSearch('userA') .then((result) => { result.total.should.be.equal(3); - result.brews.should.containSubset(ids(['BrewA', 'BrewB', 'BrewC'])); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); }); }); it('should return nothing if provided a non-exsistent user', () => { diff --git a/test/test.init.js b/test/test.init.js index 317df50..ca80e7f 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -9,15 +9,12 @@ const config = require('nconf') const Chai = require('chai') .use(require('chai-as-promised')) - .use(require('chai-subset')); + .use(require('chai-subset')) + .use(require('./brew.gen.js').chaiPlugin); const log = require('loglevel'); log.setLevel(config.get('log_level')); -//TODO: extend should to have a brewCheck -// eg. result.brews.should.haveBrews('BrewA', 'BrewB') -// Then can remove chai-subset - module.exports = { should: Chai.should() }; From 26bcb3395a495fb307e735c3bed11055ef4ef28e Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Fri, 27 Jan 2017 19:47:45 -0500 Subject: [PATCH 8/9] Fixing edge cases in the search tests --- server/brew.search.js | 24 ++++++++------- server/middleware.js | 2 +- test/admin.test.js | 9 +++--- test/api.test.js | 6 ++-- test/brew.gen.js | 4 +-- test/middleware.test.js | 6 ++-- test/search.test.js | 66 ++++++++++++++++++++++++++++++----------- test/test.init.js | 6 +++- 8 files changed, 80 insertions(+), 43 deletions(-) diff --git a/server/brew.search.js b/server/brew.search.js index 99e993e..611b1bc 100644 --- a/server/brew.search.js +++ b/server/brew.search.js @@ -21,29 +21,31 @@ module.exports = (Brew) => { return cmds.search(query, opts, fullAccess); }, - search : (queryObj={}, opts={}, fullAccess = true) => { - const pagination = _.defaults(opts.pagination, { + search : (queryObj={}, options={}, fullAccess = true) => { + const opts = _.merge({ limit : 25, - page : 0 - }); - const sorting = _.defaults(opts.sorting, { - 'views' : 1 - }); + page : 0, + sort : {} + }, options); + let filter = { text : 0 }; + if(!fullAccess){ filter.editId = 0; - queryObj.published = false; + queryObj.published = true; } const searchQuery = Brew .find(queryObj) - .sort(sorting) + .sort(opts.sort) .select(filter) - .limit(pagination.limit) - .skip(pagination.page * pagination.limit) + .limit(opts.limit) + .skip(opts.page * opts.limit) + .lean() .exec(); + const countQuery = Brew.count(queryObj).exec(); return Promise.all([searchQuery, countQuery]) diff --git a/server/middleware.js b/server/middleware.js index cc2a535..fb19fb1 100644 --- a/server/middleware.js +++ b/server/middleware.js @@ -16,7 +16,7 @@ const Middleware = { return next(); }, admin : (req, res, next) => { - if(req.query.admin_key === config.get('admin:key')){ + if(req.headers['x-homebrew-admin'] === config.get('admin:key')){ req.admin = true; } return next(); diff --git a/test/admin.test.js b/test/admin.test.js index 6d2ccce..b2590ca 100644 --- a/test/admin.test.js +++ b/test/admin.test.js @@ -15,6 +15,7 @@ let brewA = { authors : ['your_dm'] }; + describe('Admin API', ()=>{ before('Connect DB', DB.connect); @@ -35,7 +36,7 @@ describe('Admin API', ()=>{ it('looks up a brew based on the share id', () => { return request(app) .get(`/admin/lookup/${brewA.shareId}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -47,7 +48,7 @@ describe('Admin API', ()=>{ it('looks up a brew based on the edit id', () => { return request(app) .get(`/admin/lookup/${brewA.editId}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -60,7 +61,7 @@ describe('Admin API', ()=>{ const query = brewA.editId.substring(0, brewA.editId.length -2); return request(app) .get(`/admin/lookup/${query}`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const brew = res.body; @@ -72,7 +73,7 @@ describe('Admin API', ()=>{ it('throws an error if it can not find a brew', ()=>{ return request(app) .get(`/admin/lookup/BADID`) - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(404); }); }); diff --git a/test/api.test.js b/test/api.test.js index 31330e5..8017c8c 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,6 +1,6 @@ -const testing = require('./test.init.js'); +const Test = require('./test.init.js'); const request = require('supertest-as-promised'); -const jwt = require('jwt-simple'); + const config = require('nconf'); const app = require('app.js'); @@ -24,7 +24,7 @@ describe('Brew API', () => { before('Connect DB', DB.connect); before('Clear DB', BrewData.removeAll); before('Create session token', () => { - session_token = jwt.encode(test_user, config.get('jwt_secret')); + session_token = Test.getSessionToken(test_user); }); before('Create brew', ()=>{ return BrewData.create(storedBrew) diff --git a/test/brew.gen.js b/test/brew.gen.js index fa67643..0d1fc13 100644 --- a/test/brew.gen.js +++ b/test/brew.gen.js @@ -56,6 +56,7 @@ module.exports = { }, populateDB : (brewCollection)=>{ + PopulatedBrews = {}; return Promise.all(_.map(brewCollection, (brewData, id) => { return BrewData.create(brewData) .then((brew)=>{ @@ -72,8 +73,7 @@ module.exports = { const storedBrew = PopulatedBrews[brewId]; if(!storedBrew) return false; return _.some(this._obj, (brew)=>{ - return brew.editId == storedBrew.editId && - brew.shareId == storedBrew.shareId && + return brew.shareId == storedBrew.shareId && brew.title == storedBrew.title && brew.views == storedBrew.views; }); diff --git a/test/middleware.test.js b/test/middleware.test.js index 44c822d..c407516 100644 --- a/test/middleware.test.js +++ b/test/middleware.test.js @@ -15,8 +15,6 @@ const requestHandler = (req, res) => { }; -console.log(config.get('admin:key')); - const test_user = { username : 'cool guy' }; @@ -106,7 +104,7 @@ describe('Middleware', () => { app.use(mw.admin); app.use(requestHandler) return request(app).get('/') - .query({ admin_key : config.get('admin:key') }) + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const req = res.body; @@ -119,7 +117,7 @@ describe('Middleware', () => { app.get(requestHandler); app.use(Error.expressHandler); return request(app).get('/') - .query({ admin_key : 'BADUSER' }) + .set('x-homebrew-admin', 'BADADMIN') .send() .expect(401); }); diff --git a/test/search.test.js b/test/search.test.js index 058199e..04c30d9 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -17,8 +17,12 @@ describe('Brew Search', () => { describe('Searching', ()=>{ - it.skip('should return a total and a brew array', ()=>{ - + it('should return a total and a brew array', ()=>{ + return BrewData.search() + .then((result) => { + result.total.should.be.a('number'); + result.brews.should.be.an('array'); + }) }); it('should be able to search for all brews', ()=>{ @@ -32,16 +36,35 @@ describe('Brew Search', () => { }); describe('Pagniation', () => { - it.skip('should return the exact number of brews based on limit', () => { - + it('should return the exact number of brews based on limit', () => { + return BrewData.search({}, { + limit : 2 + }) + .then((result) => { + result.total.should.be.equal(_.size(BrewGen.static())); + result.brews.length.should.be.equal(2); + }) }); - it.skip('should return the correct pages when specified', () => { - + it('should return the correct pages when specified', () => { + return BrewData.search({}, { + limit : 2, + page : 1, + sort : { views : 1 } + }) + .then((result) => { + result.brews.should.have.brews('BrewA', 'BrewB'); + }) }); - it.skip('should return a partial list if on the last page', () => { - + it('should return a partial list if on the last page', () => { + return BrewData.search({}, { + limit : 3, + page : 1 + }) + .then((result) => { + result.brews.length.should.be.equal(1); + }); }); }); @@ -59,17 +82,26 @@ describe('Brew Search', () => { }); describe('Permissions', () => { - it.skip('should only fetch published brews', () => { - + it('should only fetch published brews', () => { + return BrewData.search({}, {}, false) + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewB', 'BrewD'); + }) }); - it.skip('fetched brews should not have text or editId', () => { - + it('fetched brews should not have text or editId', () => { + return BrewData.search({}, {}, false) + .then((result) => { + result.brews[0].should.not.have.property('text'); + result.brews[0].should.not.have.property('editId'); + }) }); - it.skip('if admin, fetches also non-published brews, with editid', () => { - - }); - it.skip('if author, fetches also non-published brews, with editid', ()=>{ - + it('if full access, brews should have editid, but no text', () => { + return BrewData.search({}, {}, true) + .then((result) => { + result.brews[0].should.not.have.property('text'); + result.brews[0].should.have.property('editId'); + }) }); }); diff --git a/test/test.init.js b/test/test.init.js index ca80e7f..ab1567a 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -15,6 +15,10 @@ const Chai = require('chai') const log = require('loglevel'); log.setLevel(config.get('log_level')); +const jwt = require('jwt-simple'); module.exports = { - should: Chai.should() + should: Chai.should(), + getSessionToken : (userInfo) => { + return jwt.encode(userInfo, config.get('jwt_secret')); + } }; From 75111acefb572b16378e7f1718824088fa581f04 Mon Sep 17 00:00:00 2001 From: Scott Tolksdorf Date: Sat, 28 Jan 2017 12:25:26 -0500 Subject: [PATCH 9/9] All tests should be done, phew --- server/brew.api.js | 17 ++- server/brew.search.js | 25 ++-- server/middleware.js | 2 + test/api.test.js | 304 ++++++++++++++++++++++++++++-------------- test/brew.gen.js | 23 ++++ test/search.test.js | 35 +++-- 6 files changed, 289 insertions(+), 117 deletions(-) diff --git a/server/brew.api.js b/server/brew.api.js index d7e30bf..d0565b4 100644 --- a/server/brew.api.js +++ b/server/brew.api.js @@ -6,10 +6,25 @@ const mw = require('./middleware.js'); //Search router.get('/api/brew', (req, res, next) => { + const opts = _.pick(req.query, ['limit', 'sort', 'page']); - //TODO + BrewData.termSearch(req.query.terms, opts, req.admin) + .then((result) => { + return res.status(200).json(result); + }) + .catch(next); +}); +//User +router.get('/api/user/:username', (req, res, next) => { + const fullAccess = req.admin || + !!(req.account && req.params.username == req.account.username); + BrewData.userSearch(req.params.username, fullAccess) + .then((result) => { + return res.status(200).json(result); + }) + .catch(next); }); //Get diff --git a/server/brew.search.js b/server/brew.search.js index 611b1bc..a75b768 100644 --- a/server/brew.search.js +++ b/server/brew.search.js @@ -3,30 +3,35 @@ const _ = require('lodash'); module.exports = (Brew) => { const cmds = { termSearch : (terms='', opts, fullAccess) => { - const query = {$text: { - //Wrap terms in quotes to perform an AND operation - $search: _.map(terms.split(' '), (term)=>{ - return `\"${term}\"`; - }).join(' '), - $caseSensitive : false - }}; + let query = {}; + if(terms){ + query = {$text: { + //Wrap terms in quotes to perform an AND operation + $search: _.map(terms.split(' '), (term)=>{ + return `\"${term}\"`; + }).join(' '), + $caseSensitive : false + }}; + } return cmds.search(query, opts, fullAccess); }, - userSearch : (username, opts, fullAccess) => { + userSearch : (username, fullAccess) => { const query = { authors : username }; - return cmds.search(query, opts, fullAccess); + return cmds.search(query, {}, fullAccess); }, search : (queryObj={}, options={}, fullAccess = true) => { const opts = _.merge({ limit : 25, page : 0, - sort : {} + sort : {} }, options); + opts.limit = _.toNumber(opts.limit); + opts.page = _.toNumber(opts.page); let filter = { text : 0 diff --git a/server/middleware.js b/server/middleware.js index fb19fb1..fb4c424 100644 --- a/server/middleware.js +++ b/server/middleware.js @@ -16,6 +16,7 @@ const Middleware = { return next(); }, admin : (req, res, next) => { + req.admin = false; if(req.headers['x-homebrew-admin'] === config.get('admin:key')){ req.admin = true; } @@ -44,6 +45,7 @@ const Middleware = { }, + //TODO: REMOVE //Loaders loadBrew : (req, res, next) => { BrewData.getByEdit(req.params.editId) diff --git a/test/api.test.js b/test/api.test.js index 8017c8c..0ba5fdb 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,4 +1,5 @@ const Test = require('./test.init.js'); +const _ = require('lodash'); const request = require('supertest-as-promised'); const config = require('nconf'); @@ -6,132 +7,241 @@ const config = require('nconf'); const app = require('app.js'); const DB = require('db.js'); const BrewData = require('brew.data.js'); +const BrewGen = require('./brew.gen.js'); const Error = require('error.js'); -const apiPath = '/api/brew'; -let session_token; -const test_user = { - username : 'cool guy' -}; -let storedBrew = { - title : 'good title', - text : 'original text', - authors : ['your_dm'] -}; +const UserX = { username : 'userX' }; +const UserA = { username : 'userA' }; +let UserXToken, UserAToken; describe('Brew API', () => { - before('Connect DB', DB.connect); - before('Clear DB', BrewData.removeAll); before('Create session token', () => { - session_token = Test.getSessionToken(test_user); - }); - before('Create brew', ()=>{ - return BrewData.create(storedBrew) - .then((brew)=>{ storedBrew = brew; }); + UserXToken = Test.getSessionToken(UserX); + UserAToken = Test.getSessionToken(UserA); }); + describe('CRUD', ()=>{ + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); + }); + describe('Create', () => { + it('creates a new brew', () => { + return request(app) + .post(`/api/brew`) + .send({ text : 'Brew Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').that.is.a('string'); + brew.should.have.property('shareId').that.is.a('string'); + brew.should.have.property('text').equal('Brew Text'); + brew.should.not.have.property('_id'); + }); + }); - describe('Create', () => { - it('creates a new brew', () => { - return request(app) - .post(apiPath) - .send({ text : 'Brew Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('editId').that.is.a('string'); - brew.should.have.property('shareId').that.is.a('string'); - brew.should.have.property('text').equal('Brew Text'); - brew.should.not.have.property('_id'); - }); + it('creates a new brew with a session author', () => { + return request(app) + .post(`/api/brew`) + .set('Cookie', `nc_session=${UserXToken}`) + .send({ text : 'Brew Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('authors').include(UserX.username); + }); + }); }); - it('creates a new brew with a session author', () => { - return request(app) - .post(apiPath) - .set('Cookie', `nc_session=${session_token}`) - .send({ text : 'Brew Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('authors').include(test_user.username); - }); - }); - }); + describe('Update', () => { + it('updates an existing brew', () => { + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/${storedBrew.editId}`) + .send({ text : 'New Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(storedBrew.editId); + brew.should.have.property('text').equal('New Text'); + brew.should.have.property('authors').include(storedBrew.authors[0]); + brew.should.not.have.property('_id'); + }); + }); - describe('Update', () => { - it('updates an existing brew', () => { - return request(app) - .put(`${apiPath}/${storedBrew.editId}`) - .send({ text : 'New Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('editId').equal(storedBrew.editId); - brew.should.have.property('text').equal('New Text'); - brew.should.have.property('authors').include('your_dm'); - brew.should.not.have.property('_id'); - }); + it('adds the user as author', () => { + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/${storedBrew.editId}`) + .set('Cookie', `nc_session=${UserXToken}`) + .send({ text : 'New Text' }) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('authors').include(UserX.username); + brew.should.have.property('authors').include(storedBrew.authors[0]); + }); + }); + it('should throw error on bad edit id', ()=>{ + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .put(`/api/brew/BADEDITID`) + .send({ text : 'New Text' }) + .expect(404) + }); }); - it('adds the user as author', () => { - return request(app) - .put(`${apiPath}/${storedBrew.editId}`) - .set('Cookie', `nc_session=${session_token}`) - .send({ text : 'New Text' }) - .expect(200) - .then((res) => { - const brew = res.body; - brew.should.have.property('authors').include(test_user.username); - brew.should.have.property('authors').include('your_dm'); - }); + describe('Remove', () => { + it('should removes a brew', ()=>{ + const storedBrew = BrewGen.get('BrewA'); + return request(app) + .del(`/api/brew/${storedBrew.editId}`) + .send() + .expect(200) + .then(() => { + BrewData.getByEdit(storedBrew.editId) + .then(() => { throw 'Brew found when one should not have been'; }) + .catch((err) => { + err.should.be.instanceof(Error.noBrew); + }) + }); + }); }); - it('should throw error on bad edit id', ()=>{ - return request(app) - .put(`${apiPath}/BADEDITID`) - .send({ text : 'New Text' }) - .expect(404) - }); - }); + }) - describe('Remove', () => { - it('should removes a brew', ()=>{ - return request(app) - .del(`${apiPath}/${storedBrew.editId}`) - .send() - .expect(200) - .then(() => { - BrewData.getByEdit(storedBrew.editId) - .then(() => { throw 'Brew found when one should not have been'; }) - .catch((err) => { - err.should.be.instanceof(Error.noBrew); - }) - }); - }); - }); describe('Search', () => { - it.skip('should be able to search for brews with given terms', ()=>{ - + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); }); - it.skip('should exclude unpublished brews and have no editIdsh', ()=>{ + it('should be able to search for all published brews', ()=>{ + return request(app) + .get(`/api/brew`) + .query({}) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewB','BrewD'); + result.brews[0].should.not.have.property('editId'); + }); }); - it.skip('should sort the search', ()=>{ + it('should be able to search for brews with given terms', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + terms : '5e ranger' + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(1); + result.brews.should.have.brews('BrewD'); + }); }); - it.skip('should use pagniation on the search', ()=>{ - + it('should be able to sort the search', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + sort : { views : 1} + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews[0].should.be.brew('BrewD'); + result.brews[1].should.be.brew('BrewB'); + }); + }); + it('should use pagniation on the search', ()=>{ + return request(app) + .get(`/api/brew`) + .query({ + limit : 1, + page : 1, + sort : { views : -1} + }) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(2); + result.brews[0].should.be.brew('BrewD'); + }) + }); + it('should return all brews and editIds if admin', ()=>{ + return request(app) + .get(`/api/brew`) + .query({}) + .set('x-homebrew-admin', config.get('admin:key')) + .send() + .expect(200) + .then((res) => { + const result = res.body; + const brewCount = _.size(BrewGen.static()); + result.total.should.be.equal(brewCount); + result.brews.length.should.be.equal(brewCount); + result.brews[0].should.have.property('editId'); + }); }); }); describe('User', () => { - it.skip('should be able to query brews for a specific user', ()=>{ - + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); }); - it.skip('should return full access to brews if loggedin user is queried user', ()=>{ + it('should be able to query brews for a specific user', ()=>{ + return request(app) + .get(`/api/user/userA`) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(1); + result.brews.length.should.be.equal(1); + result.brews.should.have.brews('BrewB'); + result.brews[0].should.not.have.property('editId'); + }); + }); + it('should have full access if loggedin user is queried user', ()=>{ + return request(app) + .get(`/api/user/userA`) + .set('Cookie', `nc_session=${UserAToken}`) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(3); + result.brews.length.should.be.equal(3); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); + result.brews[0].should.have.property('editId'); + }); + }); + it('should have full access if admin', ()=>{ + return request(app) + .get(`/api/user/userA`) + .set('x-homebrew-admin', config.get('admin:key')) + .send() + .expect(200) + .then((res) => { + const result = res.body; + result.total.should.be.equal(3); + result.brews.length.should.be.equal(3); + result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC'); + result.brews[0].should.have.property('editId'); + }); }); }); diff --git a/test/brew.gen.js b/test/brew.gen.js index 0d1fc13..c7d59b3 100644 --- a/test/brew.gen.js +++ b/test/brew.gen.js @@ -4,6 +4,8 @@ const BrewData = require('../server/brew.data.js'); let PopulatedBrews = {}; module.exports = { + //TODO: Add in a generator for old brews to test the old rendering code + random : (num = 20)=>{ return _.times(num, ()=>{ //TODO: Build better generator @@ -66,6 +68,10 @@ module.exports = { ); }, + get : (brewId) => { + return PopulatedBrews[brewId] + }, + chaiPlugin : (chai, utils) => { chai.Assertion.addMethod('brews', function(...brewIds){ new chai.Assertion(this._obj).to.be.instanceof(Array); @@ -84,5 +90,22 @@ module.exports = { `expect #{this} to not have brews ${brewIds.join(', ')}` ) }); + + chai.Assertion.addMethod('brew', function(brewId){ + new chai.Assertion(this._obj).to.be.instanceof(Object); + const brew = this._obj; + const storedBrew = PopulatedBrews[brewId]; + + const valid = storedBrew && + brew.shareId == storedBrew.shareId && + brew.title == storedBrew.title && + brew.views == storedBrew.views; + + this.assert( + valid, + `expect #{this} to be brew ${brewId}`, + `expect #{this} to not be brew ${brewId}` + ) + }); } }; \ No newline at end of file diff --git a/test/search.test.js b/test/search.test.js index 04c30d9..6649500 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -70,14 +70,27 @@ describe('Brew Search', () => { }); describe('Sorting', ()=>{ - it.skip('should sort ASC', () => { - + it('should sort ASC', () => { + return BrewData.search({}, { + sort : { views : 1 } + }) + .then((result) => { + result.brews[0].should.be.brew('BrewC'); + result.brews[1].should.be.brew('BrewD'); + result.brews[2].should.be.brew('BrewB'); + result.brews[3].should.be.brew('BrewA'); + }) }); - it.skip('should sort DESC', () => { - - }); - it.skip('should sort based on multiple fields', () => { - + it('should sort DESC', () => { + return BrewData.search({}, { + sort : { views : -1 } + }) + .then((result) => { + result.brews[0].should.be.brew('BrewA'); + result.brews[1].should.be.brew('BrewB'); + result.brews[2].should.be.brew('BrewD'); + result.brews[3].should.be.brew('BrewC'); + }) }); }); @@ -146,8 +159,12 @@ describe('Brew Search', () => { result.brews.should.have.brews('BrewB'); }); }); - it.skip('should not worry about the case of the terms', () => { - + it('should not worry about the case of the terms', () => { + return BrewData.termSearch('FANCY') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.have.brews('BrewA', 'BrewB'); + }); }); });