diff --git a/client/admin/admin.jsx b/client/admin/admin.jsx index 4fd8d5505..5a725b76b 100644 --- a/client/admin/admin.jsx +++ b/client/admin/admin.jsx @@ -1,38 +1,41 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); +const React = require('react'); +const _ = require('lodash'); -var HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx'); +const Nav = require('naturalcrit/nav/nav.jsx'); -var Admin = React.createClass({ +const BrewLookup = require('./brewLookup/brewLookup.jsx'); +const AdminSearch = require('./adminSearch/adminSearch.jsx'); +const InvalidBrew = require('./invalidBrew/invalidBrew.jsx'); + +const Admin = React.createClass({ getDefaultProps: function() { return { - url : "", - admin_key : "", - homebrews : [], + admin_key : '', }; }, + renderNavbar : function(){ + return + + + Homebrewery Admin + + + + }, + render : function(){ - var self = this; - return( -
+ return
+ {this.renderNavbar()} +
+ + -
-
- - naturalcrit admin -
-
+
Danger Zone
-
- - -
- - -
- ); + + +
} }); diff --git a/client/admin/admin.less b/client/admin/admin.less index c13063a01..ef7d38775 100644 --- a/client/admin/admin.less +++ b/client/admin/admin.less @@ -1,39 +1,53 @@ -@import 'naturalcrit/styles/reset.less'; -@import 'naturalcrit/styles/elements.less'; -@import 'naturalcrit/styles/animations.less'; -@import 'naturalcrit/styles/colors.less'; -@import 'naturalcrit/styles/tooltip.less'; -@import 'font-awesome/css/font-awesome.css'; - -html,body, #reactContainer, .naturalCrit{ +@import 'naturalcrit/styles/core.less'; +html,body, #reactRoot{ min-height : 100%; } - -@sidebarWidth : 250px; - body{ - background-color : #eee; - font-family : 'Open Sans', sans-serif; - color : #4b5055; - font-weight : 100; - text-rendering : optimizeLegibility; + height : 100%; margin : 0; padding : 0; - height : 100%; + background-color : #ddd; + font-family : 'Open Sans', sans-serif; + font-weight : 100; + color : #4b5055; + text-rendering : optimizeLegibility; } - -.admin{ - - header{ +.admin { + nav { background-color : @red; - font-size: 2em; - padding : 20px 0px; - color : white; - margin-bottom: 30px; - i{ - margin-right: 30px; + .navItem{ + background-color : @red; + } + .homebreweryLogo{ + font-family : CodeBold; + font-size : 12px; + color : white; + div{ + margin-top : 2px; + margin-bottom : -2px; + } + } + } + h1{ + margin-bottom : 10px; + font-size : 2em; + font-weight : 800; + border-bottom : 1px solid #ddd; + } + main.content{ + width : 1000px; + margin : 0 auto; + padding : 50px 20px; + background-color : white; + .dangerZone{ + margin : 30px 0px; + padding : 10px 20px; + background : repeating-linear-gradient(45deg, @yellow, @yellow 10px, darken(#333, 10%) 10px, darken(#333, 10%) 20px); + font-size : 1em; + font-weight : 800; + color : white; + text-transform : uppercase; } } - } \ No newline at end of file diff --git a/client/admin/adminSearch/adminSearch.jsx b/client/admin/adminSearch/adminSearch.jsx new file mode 100644 index 000000000..a9ab8764c --- /dev/null +++ b/client/admin/adminSearch/adminSearch.jsx @@ -0,0 +1,86 @@ + +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); +const request = require('superagent'); + +const BrewTable = require('../brewTable/brewTable.jsx'); + +const LIMIT = 10; + +const AdminSearch = React.createClass({ + getDefaultProps: function() { + return { + adminKey : '', + }; + }, + getInitialState: function() { + return { + totalBrews : 1, + brews: [], + + searching : false, + error : null, + + page : 1, + searchTerms : '' + }; + }, + + handleSearch : function(e){ + this.setState({ + searchTerms : e.target.value + }); + }, + handlePage : function(e){ + this.setState({ + page : e.target.value + }); + }, + + search : function(){ + this.setState({ searching : true, error : null }); + + request.get(`/api/brew`) + .query({ + terms : this.state.searchTerms, + limit : LIMIT, + page : this.state.page - 1 + }) + .set('x-homebrew-admin', this.props.adminKey) + .end((err, res) => { + if(err){ + this.setState({ + searching : false, + error : err && err.toString() + }); + }else{ + this.setState({ + brews : res.body.brews, + totalBrews : res.body.total + }); + } + }); + }, + + render: function(){ + return
+

Admin Search

+
+ + + + + +
+ page: + + / {Math.ceil(this.state.totalBrews / LIMIT)} +
+
+ +
+ } +}); + +module.exports = AdminSearch; diff --git a/client/admin/adminSearch/adminSearch.less b/client/admin/adminSearch/adminSearch.less new file mode 100644 index 000000000..b66896d9e --- /dev/null +++ b/client/admin/adminSearch/adminSearch.less @@ -0,0 +1,17 @@ + +.adminSearch{ + .controls{ + margin-bottom : 20px; + input.search{ + height : 33px; + padding : 10px; + } + .page { + float : right; + font-weight : 800; + input{ + width : 20px; + } + } + } +} \ No newline at end of file diff --git a/client/admin/homebrewAdmin/brewLookup/brewLookup.jsx b/client/admin/brewLookup/brewLookup.jsx similarity index 66% rename from client/admin/homebrewAdmin/brewLookup/brewLookup.jsx rename to client/admin/brewLookup/brewLookup.jsx index ad43b13b6..abb38dc24 100644 --- a/client/admin/homebrewAdmin/brewLookup/brewLookup.jsx +++ b/client/admin/brewLookup/brewLookup.jsx @@ -3,8 +3,7 @@ const _ = require('lodash'); const cx = require('classnames'); const request = require('superagent'); -const Moment = require('moment'); - +const BrewTable = require('../brewTable/brewTable.jsx'); const BrewLookup = React.createClass({ getDefaultProps: function() { @@ -16,7 +15,8 @@ const BrewLookup = React.createClass({ return { query:'', resultBrew : null, - searching : false + searching : false, + error : null }; }, @@ -26,13 +26,14 @@ const BrewLookup = React.createClass({ }) }, lookup : function(){ - this.setState({ searching : true }); + this.setState({ searching : true, error : null }); request.get(`/admin/lookup/${this.state.query}`) - .query({ admin_key : this.props.adminKey }) + .set('x-homebrew-admin', this.props.adminKey) .end((err, res) => { this.setState({ searching : false, + error : err && err.toString(), resultBrew : (err ? null : res.body) }); }) @@ -42,14 +43,31 @@ const BrewLookup = React.createClass({ if(this.state.searching) return
; if(!this.state.resultBrew) return
No brew found.
; + return + + /* const brew = this.state.resultBrew; return
{brew.title}
{brew.authors.join(', ')}
- - + +
{Moment(brew.updatedAt).fromNow()}
{brew.views}
+
+
+ +
+
+
+ */ + }, + + renderError : function(){ + if(!this.state.error) return; + + return
+ {this.state.error}
}, @@ -60,6 +78,7 @@ const BrewLookup = React.createClass({ {this.renderFoundBrew()} + {this.renderError()} } }); diff --git a/client/admin/brewLookup/brewLookup.less b/client/admin/brewLookup/brewLookup.less new file mode 100644 index 000000000..87434c42b --- /dev/null +++ b/client/admin/brewLookup/brewLookup.less @@ -0,0 +1,13 @@ + +.brewLookup{ + height : 200px; + input{ + height : 33px; + margin-bottom : 20px; + padding : 0px 10px; + } + .error{ + font-weight : 800; + color : @red; + } +} \ No newline at end of file diff --git a/client/admin/brewTable/brewTable.jsx b/client/admin/brewTable/brewTable.jsx new file mode 100644 index 000000000..dfa450041 --- /dev/null +++ b/client/admin/brewTable/brewTable.jsx @@ -0,0 +1,54 @@ +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const Moment = require('moment'); + +//TODO: Add in delete + +const BrewTable = React.createClass({ + getDefaultProps: function() { + return { + brews : [] + }; + }, + renderRows : function(){ + return _.map(this.props.brews, (brew) => { + let authors = 'None.'; + if(brew.authors && brew.authors.length) authors = brew.authors.join(', '); + + return + {brew.title} + {authors} + {brew.editId} + {brew.shareId} + {Moment(brew.updatedAt).fromNow()} + {brew.views} + + + + + + }); + }, + render: function(){ + return + + + + + + + + + + + + + {this.renderRows()} + +
TitleAuthorsEdit LinkShare LinkLast UpdatedViewsRemove
+ } +}); + +module.exports = BrewTable; diff --git a/client/admin/brewTable/brewTable.less b/client/admin/brewTable/brewTable.less new file mode 100644 index 000000000..927242b15 --- /dev/null +++ b/client/admin/brewTable/brewTable.less @@ -0,0 +1,44 @@ + +table.brewTable{ + th{ + padding : 10px; + background-color : fade(@blue, 20%); + font-weight : 800; + } + tr:nth-child(even){ + background-color : fade(@green, 10%); + } + tr.isEmpty{ + background-color : fade(@red, 30%); + } + td{ + min-width : 100px; + padding : 10px; + text-align : center; + + /* + &.preview{ + position : relative; + &:hover{ + .content{ + display : block; + } + } + .content{ + position : absolute; + display : none; + top : 100%; + left : 0px; + z-index : 1000; + max-height : 500px; + width : 300px; + padding : 30px; + background-color : white; + font-family : monospace; + text-align : left; + pointer-events : none; + } + } + */ + } +} \ No newline at end of file diff --git a/client/admin/homebrewAdmin/brewLookup/brewLookup.less b/client/admin/homebrewAdmin/brewLookup/brewLookup.less deleted file mode 100644 index 071028c20..000000000 --- a/client/admin/homebrewAdmin/brewLookup/brewLookup.less +++ /dev/null @@ -1,8 +0,0 @@ -.brewLookup{ - height : 200px; - input{ - height : 33px; - padding : 0px 10px; - margin-bottom: 20px; - } -} \ No newline at end of file diff --git a/client/admin/homebrewAdmin/brewSearch.jsx b/client/admin/homebrewAdmin/brewSearch.jsx deleted file mode 100644 index f2334d7f5..000000000 --- a/client/admin/homebrewAdmin/brewSearch.jsx +++ /dev/null @@ -1,72 +0,0 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); - -var request = require('superagent'); - -var BrewSearch = React.createClass({ - - getDefaultProps: function() { - return { - admin_key : '' - }; - }, - - getInitialState: function() { - return { - searchTerm: '', - brew : null, - searching : false - }; - }, - - - search : function(){ - this.setState({ - searching : true - }); - - request.get('/homebrew/api/search?id=' + this.state.searchTerm) - .query({ - admin_key : this.props.admin_key, - }) - .end((err, res)=>{ - console.log(err, res, res.body.brews[0]); - this.setState({ - brew : res.body.brews[0], - - searching : false - }) - }); - }, - - handleChange : function(e){ - this.setState({ - searchTerm : e.target.value - }); - }, - handleSearchClick : function(){ - this.search(); - }, - - renderBrew : function(){ - if(!this.state.brew) return null; - return
-
Edit id : {this.state.brew.editId}
-
Share id : {this.state.brew.shareId}
-
- }, - - render : function(){ - return
- - - - - {this.renderBrew()} -
- }, - -}); - -module.exports = BrewSearch; \ No newline at end of file diff --git a/client/admin/homebrewAdmin/homebrewAdmin.jsx b/client/admin/homebrewAdmin/homebrewAdmin.jsx deleted file mode 100644 index 70b593c62..000000000 --- a/client/admin/homebrewAdmin/homebrewAdmin.jsx +++ /dev/null @@ -1,172 +0,0 @@ -var React = require('react'); -var _ = require('lodash'); -var cx = require('classnames'); -var request = require('superagent'); - -var Moment = require('moment'); - -var BrewSearch = require('./brewSearch.jsx'); - -var BrewLookup = require('./brewLookup/brewLookup.jsx'); - - -var HomebrewAdmin = React.createClass({ - getDefaultProps: function() { - return { - admin_key : '' - }; - }, - - getInitialState: function() { - return { - page: 0, - count : 20, - brewCache : {}, - total : 0, - - processingOldBrews : false - }; - }, - - - fetchBrews : function(page){ - request.get('/api/search') - .query({ - admin_key : this.props.admin_key, - count : this.state.count, - page : page - }) - .end((err, res)=>{ - if(err || !res.body || !res.body.brews) return; - this.state.brewCache[page] = res.body.brews; - this.setState({ - brewCache : this.state.brewCache, - total : res.body.total, - count : res.body.count - }) - }) - }, - - componentDidMount: function() { - this.fetchBrews(this.state.page); - }, - - changePageTo : function(page){ - if(!this.state.brewCache[page]){ - this.fetchBrews(page); - } - this.setState({ - page : page - }) - }, - - - clearInvalidBrews : function(){ - request.get('/api/invalid') - .query({admin_key : this.props.admin_key}) - .end((err, res)=>{ - if(!confirm("This will remove " + res.body.count + " brews. Are you sure?")) return; - request.get('/api/invalid') - .query({admin_key : this.props.admin_key, do_it : true}) - .end((err, res)=>{ - alert("Done!") - }); - }); - }, - - - deleteBrew : function(brewId){ - if(!confirm("Are you sure you want to delete '" + brewId + "'?")) return; - request.get('/api/remove/' + brewId) - .query({admin_key : this.props.admin_key}) - .end(function(err, res){ - window.location.reload(); - }) - }, - - handlePageChange : function(dir){ - this.changePageTo(this.state.page + dir); - }, - - - renderPagnination : function(){ - var outOf; - if(this.state.total){ - outOf = this.state.page + ' / ' + Math.round(this.state.total/this.state.count); - } - return
- - {outOf} - -
- }, - - - renderBrews : function(){ - var brews = this.state.brewCache[this.state.page] || _.times(this.state.count); - return _.map(brews, (brew)=>{ - return - {brew.editId} - {brew.shareId} - {Moment(brew.createdAt).fromNow()} - {Moment(brew.updatedAt).fromNow()} - {Moment(brew.lastViewed).fromNow()} - {brew.views} - -
- -
- - - }); - }, - - renderBrewTable : function(){ - return
- - - - - - - - - - - - - {this.renderBrews()} - -
Edit IdShare IdCreated AtLast UpdatedLast ViewedViews
-
- }, - - render : function(){ - var self = this; - return
- - - - {/* -

- Homebrews - {this.state.total} -

- - - - - - {this.renderPagnination()} - {this.renderBrewTable()} - - - - - */} -
- } -}); - -module.exports = HomebrewAdmin; diff --git a/client/admin/homebrewAdmin/homebrewAdmin.less b/client/admin/homebrewAdmin/homebrewAdmin.less deleted file mode 100644 index 6cfc3d5a0..000000000 --- a/client/admin/homebrewAdmin/homebrewAdmin.less +++ /dev/null @@ -1,53 +0,0 @@ - -.homebrewAdmin{ - margin-bottom: 80px; - .brewTable{ - table{ - - th{ - padding : 10px; - font-weight : 800; - } - tr:nth-child(even){ - background-color : fade(@green, 10%); - } - tr.isEmpty{ - background-color : fade(@red, 30%); - } - td{ - min-width : 100px; - padding : 10px; - text-align : center; - - &.preview{ - position : relative; - &:hover{ - .content{ - display : block; - } - } - .content{ - position : absolute; - display : none; - top : 100%; - left : 0px; - z-index : 1000; - max-height : 500px; - width : 300px; - padding : 30px; - background-color : white; - font-family : monospace; - text-align : left; - pointer-events : none; - } - } - } - } - } - .deleteButton{ - cursor: pointer; - } - button.clearOldButton{ - float : right; - } -} \ No newline at end of file diff --git a/client/admin/invalidBrew/invalidBrew.jsx b/client/admin/invalidBrew/invalidBrew.jsx new file mode 100644 index 000000000..28ae01d93 --- /dev/null +++ b/client/admin/invalidBrew/invalidBrew.jsx @@ -0,0 +1,54 @@ +const React = require('react'); +const _ = require('lodash'); +const cx = require('classnames'); + +const request = require('superagent'); +const BrewTable = require('../brewTable/brewTable.jsx'); + + +const InvalidBrew = React.createClass({ + getDefaultProps: function() { + return { + adminKey : '', + }; + }, + getInitialState: function() { + return { + brews: [] + }; + }, + getInvalid : function(){ + request.get(`/admin/invalid`) + .set('x-homebrew-admin', this.props.adminKey) + .end((err, res) => { + this.setState({ + brews : res.body + }); + }) + }, + removeInvalid : function(){ + if(!this.state.brews.length) return; + if(!confirm(`Are you sure you want to remove ${this.state.brews.length} brews`)) return; + if(!confirm('Sure you are sure?')) return; + + request.delete(`/admin/invalid`) + .set('x-homebrew-admin', this.props.adminKey) + .end((err, res) => { + console.log(err, res.body); + alert('Invalid brews removed!'); + this.getInvalid(); + }) + }, + render: function(){ + return
+

Remove Invalid Brews

+
This will removes all brews older than 3 days and shorter than a tweet.
+ + + + +
+ } +}); + +module.exports = InvalidBrew; diff --git a/client/admin/invalidBrew/invalidBrew.less b/client/admin/invalidBrew/invalidBrew.less new file mode 100644 index 000000000..495d04f32 --- /dev/null +++ b/client/admin/invalidBrew/invalidBrew.less @@ -0,0 +1,5 @@ +.invalidBrew{ + button{ + margin: 10px 4px; + } +} \ No newline at end of file diff --git a/client/homebrew/homebrew.jsx b/client/homebrew/homebrew.jsx index 97f66bb0a..4cf976973 100644 --- a/client/homebrew/homebrew.jsx +++ b/client/homebrew/homebrew.jsx @@ -37,7 +37,6 @@ const Homebrew = React.createClass({ loginPath : this.props.loginPath }); - Router = CreateRouter({ '/edit/:id' : , '/share/:id' : , diff --git a/client/homebrew/pages/userPage/userPage.jsx b/client/homebrew/pages/userPage/userPage.jsx index b6bdbeffc..6dd386d5a 100644 --- a/client/homebrew/pages/userPage/userPage.jsx +++ b/client/homebrew/pages/userPage/userPage.jsx @@ -52,6 +52,7 @@ const UserPage = React.createClass({ render : function(){ const brews = this.getSortedBrews(); + console.log('user brews', brews); return
diff --git a/package.json b/package.json index e553ba58f..02d5ab858 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", + "populate": "node scripts/populate.js", "prod": "set NODE_ENV=production&& npm run build", "postinstall": "npm run build", "start": "node server.js", @@ -43,6 +44,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/scripts/populate.js b/scripts/populate.js new file mode 100644 index 000000000..e002590ad --- /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 BrewGen.populateDB(BrewGen.random(50)); + }) + .then(() => { + console.log('Adding specific brews...'); + return BrewGen.populateDB(BrewGen.static()); + }) + .then(() => { + return DB.close(); + }) + .catch(console.error); diff --git a/server/admin.routes.js b/server/admin.routes.js index a526e5e5e..cf3cf85eb 100644 --- a/server/admin.routes.js +++ b/server/admin.routes.js @@ -3,10 +3,21 @@ const router = require('express').Router(); const vitreumRender = require('vitreum/steps/render'); const templateFn = require('../client/template.js'); const config = require('nconf'); +const Moment = require('moment'); const mw = require('./middleware.js'); const BrewData = require('./brew.data.js'); + +const getInvalidBrewQuery = ()=>{ + return BrewData.model.find({ + '$where' : "this.text.length < 140", + createdAt: { + $lt: Moment().subtract(3, 'days').toDate() + } + }).select({ text : false }); +} + router.get('/admin', mw.adminLogin, (req, res, next) => { return vitreumRender('admin', templateFn, { url : req.originalUrl, @@ -19,12 +30,29 @@ router.get('/admin', mw.adminLogin, (req, res, next) => { }); //Removes all empty brews that are older than 3 days and that are shorter than a tweet +router.get('/admin/invalid', mw.adminOnly, (req, res, next)=>{ + getInvalidBrewQuery().exec() + .then((brews) => { + return res.json(brews); + }) + .catch(next); +}); router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{ - BrewData.removeInvalid(!!req.query.do_it) - .then((removedCount) => { - return res.join({ - count : removedCount - }); + getInvalidBrewQuery().remove() + .then(()=>{ + return res.status(200).send(); + }) + .catch(next); +}); + +router.get('/admin/lookup/:search', mw.adminOnly, (req, res, next) => { + BrewData.get({ $or:[ + //Searches for partial matches on either edit or share + {editId : { "$regex": req.params.search, "$options": "i" }}, + {shareId : { "$regex": req.params.search, "$options": "i" }}, + ]}) + .then((brews) => { + return res.json(brews); }) .catch(next); }); diff --git a/server/brew.api.js b/server/brew.api.js index d7e30bf6b..d0565b4e4 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.data.js b/server/brew.data.js index a6408853e..c6e92ee3f 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,9 @@ const BrewSchema = mongoose.Schema({ } }); +//Index these fields for fast text searching +BrewSchema.index({ title: "text", description: "text" }); + BrewSchema.methods.increaseView = function(){ this.views = this.views + 1; return this.save(); @@ -39,9 +43,6 @@ BrewSchema.methods.increaseView = function(){ const Brew = mongoose.model('Brew', BrewSchema); - - - const BrewData = { schema : BrewSchema, model : Brew, @@ -65,6 +66,9 @@ const BrewData = { delete newBrew.shareId; delete newBrew.editId; brew = _.merge(brew, newBrew, { updatedAt : Date.now() }); + + brew.markModified('authors'); + brew.markModified('systems'); return brew.save(); }); }, @@ -89,30 +93,8 @@ const BrewData = { getByEdit : (editId) => { return BrewData.get({ editId }); }, - - //Removes all empty brews that are older than 3 days and that are shorter than a tweet - removeInvalid : (force = false) => { - const invalidBrewQuery = Brew.find({ - '$where' : "this.text.length < 140", - createdAt: { - $lt: Moment().subtract(3, 'days').toDate() - } - }); - if(force) return invalidBrewQuery.remove().exec(); - return invalidBrewQuery.exec() - .then((objs) => { - return objs.length; - }); - }, - - search : (query, req={}) => { - //defaults with page and count - //returns a non-text version of brews - //assume sanatized ? - return Promise.resolve([]); - }, - - }; -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 000000000..a75b768a2 --- /dev/null +++ b/server/brew.search.js @@ -0,0 +1,66 @@ +const _ = require('lodash'); + +module.exports = (Brew) => { + const cmds = { + termSearch : (terms='', opts, fullAccess) => { + 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, fullAccess) => { + const query = { + authors : username + }; + + return cmds.search(query, {}, fullAccess); + }, + + search : (queryObj={}, options={}, fullAccess = true) => { + const opts = _.merge({ + limit : 25, + page : 0, + sort : {} + }, options); + opts.limit = _.toNumber(opts.limit); + opts.page = _.toNumber(opts.page); + + let filter = { + text : 0 + }; + + if(!fullAccess){ + filter.editId = 0; + queryObj.published = true; + } + + const searchQuery = Brew + .find(queryObj) + .sort(opts.sort) + .select(filter) + .limit(opts.limit) + .skip(opts.page * opts.limit) + .lean() + .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 a9c540050..d0756bfc9 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.'); @@ -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/server/middleware.js b/server/middleware.js index cc2a53589..fb4c42418 100644 --- a/server/middleware.js +++ b/server/middleware.js @@ -16,7 +16,8 @@ const Middleware = { return next(); }, admin : (req, res, next) => { - if(req.query.admin_key === config.get('admin:key')){ + req.admin = false; + if(req.headers['x-homebrew-admin'] === config.get('admin:key')){ req.admin = true; } return next(); @@ -44,6 +45,7 @@ const Middleware = { }, + //TODO: REMOVE //Loaders loadBrew : (req, res, next) => { BrewData.getByEdit(req.params.editId) diff --git a/shared/homebrewery/account.store.js b/shared/homebrewery/account.store.js index b79bd9f6e..c1813f746 100644 --- a/shared/homebrewery/account.store.js +++ b/shared/homebrewery/account.store.js @@ -16,7 +16,6 @@ Store.init = (state)=>{ Store.getLoginPath = ()=>{ let path = State.loginPath; if(typeof window !== 'undefined'){ - console.log('yo here'); path = `${path}?redirect=${encodeURIComponent(window.location.href)}`; } return path; diff --git a/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less b/shared/homebrewery/brewEditor/metadataEditor/metadataEditor.less index c3141f0d4..2f9812d68 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 diff --git a/test/admin.test.js b/test/admin.test.js new file mode 100644 index 000000000..b2590caa5 --- /dev/null +++ b/test/admin.test.js @@ -0,0 +1,90 @@ +const testing = require('./test.init.js'); +const request = require('supertest-as-promised'); + +const config = require('nconf'); + +const app = require('app.js'); +const DB = require('db.js'); +const BrewData = require('brew.data.js'); +const Error = require('error.js'); + + +let brewA = { + title : 'good title', + text : 'original text', + authors : ['your_dm'] +}; + + +describe('Admin API', ()=>{ + before('Connect DB', DB.connect); + + + describe('Brew Lookup', ()=>{ + before('Clear DB', BrewData.removeAll); + before('Create brew', ()=>{ + return BrewData.create(brewA) + .then((brew)=>{ brewA = brew; }); + }); + + + it('throws an error if not admin', ()=>{ + return request(app) + .get(`/admin/lookup/${brewA.editId}`) + .expect(401); + }); + it('looks up a brew based on the share id', () => { + return request(app) + .get(`/admin/lookup/${brewA.shareId}`) + .set('x-homebrew-admin', config.get('admin:key')) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('looks up a brew based on the edit id', () => { + return request(app) + .get(`/admin/lookup/${brewA.editId}`) + .set('x-homebrew-admin', config.get('admin:key')) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('looks up a brew based on a partial id', () => { + const query = brewA.editId.substring(0, brewA.editId.length -2); + return request(app) + .get(`/admin/lookup/${query}`) + .set('x-homebrew-admin', config.get('admin:key')) + .expect(200) + .then((res) => { + const brew = res.body; + brew.should.have.property('editId').equal(brewA.editId); + brew.should.have.property('shareId').equal(brewA.shareId); + brew.should.have.property('text').equal(brewA.text); + }); + }); + it('throws an error if it can not find a brew', ()=>{ + return request(app) + .get(`/admin/lookup/BADID`) + .set('x-homebrew-admin', config.get('admin:key')) + .expect(404); + }); + }); + + describe('Invalid Brew', ()=>{ + before('Clear DB', BrewData.removeAll); + before('Create brew', ()=>{ + return BrewData.create(brewA) + .then((brew)=>{ brewA = brew; }); + }); + }); + + +}); \ No newline at end of file diff --git a/test/api.test.js b/test/api.test.js index 822683c58..0ba5fdbe5 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -1,112 +1,246 @@ -const testing = require('./test.init.js'); +const Test = require('./test.init.js'); +const _ = require('lodash'); const request = require('supertest-as-promised'); -const jwt = require('jwt-simple'); + 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 = jwt.encode(test_user, config.get('jwt_secret')); - }); - 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', ()=>{ + + describe('Search', () => { + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); + }); + + it('should be able to search for all published brews', ()=>{ return request(app) - .del(`${apiPath}/${storedBrew.editId}`) + .get(`/api/brew`) + .query({}) .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); - }) + .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('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('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', () => { + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); + }); + + 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 new file mode 100644 index 000000000..c7d59b309 --- /dev/null +++ b/test/brew.gen.js @@ -0,0 +1,111 @@ +const _ = require('lodash'); +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 + 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)=>{ + PopulatedBrews = {}; + return Promise.all(_.map(brewCollection, (brewData, id) => { + return BrewData.create(brewData) + .then((brew)=>{ + PopulatedBrews[id] = brew; + }); + }) + ); + }, + + get : (brewId) => { + return PopulatedBrews[brewId] + }, + + 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.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(', ')}` + ) + }); + + 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/middleware.test.js b/test/middleware.test.js index c4fd820eb..c40751602 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' }; @@ -105,8 +103,8 @@ describe('Middleware', () => { it('should detect when you use the admin key', () => { app.use(mw.admin); app.use(requestHandler) - return request(app).get(`/?admin_key=${config.get('admin:key')}`) - .send() + return request(app).get('/') + .set('x-homebrew-admin', config.get('admin:key')) .expect(200) .then((res) => { const req = res.body; @@ -118,7 +116,8 @@ describe('Middleware', () => { app.use(mw.adminOnly); app.get(requestHandler); app.use(Error.expressHandler); - return request(app).get(`/?admin_key=BADKEY`) + return request(app).get('/') + .set('x-homebrew-admin', 'BADADMIN') .send() .expect(401); }); diff --git a/test/search.test.js b/test/search.test.js index e69de29bb..66495002b 100644 --- a/test/search.test.js +++ b/test/search.test.js @@ -0,0 +1,186 @@ +const Test = require('./test.init.js'); +const _ = require('lodash'); + +const DB = require('db.js'); +const BrewData = require('brew.data.js'); +const BrewGen = require('./brew.gen.js'); +//const Error = require('error.js'); + + + +describe('Brew Search', () => { + before('Connect DB', DB.connect); + before('Clear DB', BrewData.removeAll); + before('Populate brews', ()=>{ + return BrewGen.populateDB(BrewGen.static()); + }); + + + describe('Searching', ()=>{ + 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', ()=>{ + return BrewData.search() + .then((result) => { + const brewCount = _.size(BrewGen.static()); + result.total.should.be.equal(brewCount); + result.brews.length.should.be.equal(brewCount); + }) + }); + }); + + describe('Pagniation', () => { + 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('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('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); + }); + }); + + }); + + describe('Sorting', ()=>{ + 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('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'); + }) + }); + }); + + describe('Permissions', () => { + 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('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('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'); + }) + }); + }); + + + + describe('Term Search', ()=>{ + it('should search brews based on title', () => { + return BrewData.termSearch('Charlie') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.have.brews('BrewC'); + }) + }); + + it('should search brews based on description', () => { + return BrewData.termSearch('fancy') + .then((result) => { + result.total.should.be.equal(2); + result.brews.should.have.brews('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.have.brews('BrewD'); + }) + }); + + it('should perform an AND operation on the provided terms', () => { + 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('Brew Beta fancy') + .then((result) => { + result.total.should.be.equal(1); + result.brews.should.have.brews('BrewB'); + }); + }); + 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'); + }); + }); + }); + + 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.have.brews('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 diff --git a/test/test.init.js b/test/test.init.js index 7dda4f811..ab1567a1b 100644 --- a/test/test.init.js +++ b/test/test.init.js @@ -7,10 +7,18 @@ 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 Chai = require('chai') + .use(require('chai-as-promised')) + .use(require('chai-subset')) + .use(require('./brew.gen.js').chaiPlugin); + const log = require('loglevel'); log.setLevel(config.get('log_level')); +const jwt = require('jwt-simple'); module.exports = { - should: should + should: Chai.should(), + getSessionToken : (userInfo) => { + return jwt.encode(userInfo, config.get('jwt_secret')); + } };