diff --git a/server/achievements/Achievement.coffee b/server/achievements/Achievement.coffee index 5dcc19d8c..47e6efdb9 100644 --- a/server/achievements/Achievement.coffee +++ b/server/achievements/Achievement.coffee @@ -2,6 +2,7 @@ mongoose = require('mongoose') jsonschema = require('../../app/schemas/models/achievement') log = require 'winston' util = require '../../app/lib/utils' +plugins = require('../plugins/plugins') # `pre` and `post` are not called for update operations executed directly on the database, # including `Model.update`,`.findByIdAndUpdate`,`.findOneAndUpdate`, `.findOneAndRemove`,and `.findByIdAndRemove`.order @@ -35,9 +36,7 @@ AchievementSchema.pre('save', (next) -> next() ) -module.exports = Achievement = mongoose.model('Achievement', AchievementSchema) - -plugins = require('../plugins/plugins') - AchievementSchema.plugin(plugins.NamedPlugin) AchievementSchema.plugin(plugins.SearchablePlugin, {searchable: ['name']}) + +module.exports = Achievement = mongoose.model('Achievement', AchievementSchema) diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee index e8fdbd668..70d669db1 100644 --- a/server/commons/Handler.coffee +++ b/server/commons/Handler.coffee @@ -17,6 +17,7 @@ module.exports = class Handler postEditableProperties: [] jsonSchema: {} waterfallFunctions: [] + allowedMethods: ['GET', 'POST', 'PUT', 'PATCH'] # subclasses should override these methods hasAccess: (req) -> true @@ -420,3 +421,7 @@ module.exports = class Handler dict[document.id] = document res.send dict res.end() + + delete: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, "DELETE not allowed." + + head: (req, res) -> @sendMethodNotAllowed res, @allowedMethods, "HEAD not allowed." diff --git a/server/commons/errors.coffee b/server/commons/errors.coffee index 8af347126..3f60ef852 100644 --- a/server/commons/errors.coffee +++ b/server/commons/errors.coffee @@ -17,8 +17,9 @@ module.exports.notFound = (res, message='Not found.') -> res.send 404, message res.end() -module.exports.badMethod = (res, message='Method Not Allowed') -> - # TODO: The response MUST include an Allow header containing a list of valid methods for the requested resource +module.exports.badMethod = (res, allowed=['GET', 'POST', 'PUT', 'PATCH'], message='Method Not Allowed') -> + allowHeader = _.reduce allowed, ((str, current) -> str += ', ' + current) + res.set 'Allow', allowHeader # TODO not sure if these are always the case res.send 405, message res.end() @@ -40,4 +41,4 @@ module.exports.gatewayTimeoutError = (res, message="Gateway timeout") -> module.exports.clientTimeout = (res, message="The server did not recieve the client response in a timely manner") -> res.send 408, message - res.end() \ No newline at end of file + res.end() diff --git a/server/routes/file.coffee b/server/routes/file.coffee index 1cc0f73d7..e96a2453a 100644 --- a/server/routes/file.coffee +++ b/server/routes/file.coffee @@ -8,7 +8,7 @@ module.exports.setup = (app) -> app.all '/file*', (req, res) -> return fileGet(req, res) if req.route.method is 'get' return filePost(req, res) if req.route.method is 'post' - return errors.badMethod(res) + return errors.badMethod(res, ['GET', 'POST']) fileGet = (req, res) -> diff --git a/test/server/common.coffee b/test/server/common.coffee index d80548aa3..c098650ca 100644 --- a/test/server/common.coffee +++ b/test/server/common.coffee @@ -30,6 +30,8 @@ models_path = [ '../../server/levels/thangs/LevelThangType' '../../server/users/User' '../../server/patches/Patch' + '../../server/achievements/Achievement' + '../../server/achievements/EarnedAchievement' ] for m in models_path @@ -162,4 +164,4 @@ tick = -> mongoose.disconnect() clearTimeout tickInterval -tickInterval = setInterval tick, 1000 \ No newline at end of file +tickInterval = setInterval tick, 1000 diff --git a/test/server/functional/achievement.spec.coffee b/test/server/functional/achievement.spec.coffee new file mode 100644 index 000000000..1ee3572d8 --- /dev/null +++ b/test/server/functional/achievement.spec.coffee @@ -0,0 +1,86 @@ +require '../common' + +describe 'Achievement', -> + + unlockable = + name: 'One Time Only' + description: 'So you did the really cool thing.' + worth: 6.66 + collection: 'level.session' + + repeatable = + name: 'Lots of em' + description: 'Oops you did it again.' + worth: 1 + collection: 'User' + proportionalTo: '_id' + + url = getURL('/db/achievement') + allowHeader = 'GET, POST, PUT, PATCH' + + it 'preparing test: deleting all Achievements first', (done) -> + clearModels [Achievement], (err) -> + expect(err).toBeNull() + done() + + it 'can\'t be created by ordinary users', (done) -> + loginJoe -> + request.post {uri: url, json: unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(403) + done() + + it 'can\'t be updated by ordinary users', (done) -> + loginJoe -> + request.put {uri: url, json:unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(403) + + request {method: 'patch', uri: url, json: unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(403) + done() + + it 'can be created by admins', (done) -> + loginAdmin -> + request.post {uri: url, json: unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(200) + unlockable._id = body._id + done() + + it 'can get all for ordinary users', (done) -> + loginJoe -> + request.get {uri: url, json: unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.length).toBe(1) + done() + + it 'can be read by ordinary users', (done) -> + loginJoe -> + request.get {uri: url + '/' + unlockable._id, json: unlockable}, (err, res, body) -> + expect(res.statusCode).toBe(200) + expect(body.name).toBe(unlockable.name) + done() + + it 'can\'t be requested with HTTP HEAD method', (done) -> + loginJoe -> + request.head {uri: url + '/' + unlockable._id}, (err, res, body) -> + expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) + done() + + it 'can\'t be requested with HTTP DEL method', (done) -> + loginJoe -> + request.del {uri: url + '/' + unlockable._id}, (err, res, body) -> + expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) + done() + + it 'get schema', (done) -> + request.get {uri:url + '/schema'}, (err, res, body) -> + expect(res.statusCode).toBe(200) + body = JSON.parse(body) + expect(body.type).toBeDefined() + done() + + it 'cleaning up test: deleting all Achievements', (done) -> + clearModels [Achievement], (err) -> + expect(err).toBeNull() + done() diff --git a/test/server/functional/file.spec.coffee b/test/server/functional/file.spec.coffee index b22990067..eba3d24e4 100644 --- a/test/server/functional/file.spec.coffee +++ b/test/server/functional/file.spec.coffee @@ -28,6 +28,8 @@ xdescribe '/file', -> my_buffer_url: 'http://fc07.deviantart.net/fs37/f/2008/283/5/1/Chu_Chu_Pikachu_by_angelishi.gif' } + allowHeader = 'GET, POST' + it 'preparing test : deletes all the files first', (done) -> dropGridFS -> done() @@ -147,19 +149,28 @@ xdescribe '/file', -> request.post(options, func) + it ' can\'t be requested with HTTP PATCH method', (done) -> + request {method: 'patch', uri:url}, (err, res) -> + expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) + done() + it ' can\'t be requested with HTTP PUT method', (done) -> request.put {uri:url}, (err, res) -> expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) done() it ' can\'t be requested with HTTP HEAD method', (done) -> request.head {uri:url}, (err, res) -> expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) done() it ' can\'t be requested with HTTP DEL method', (done) -> request.del {uri:url}, (err, res) -> expect(res.statusCode).toBe(405) + expect(res.headers.allow).toBe(allowHeader) done() # TODO: test server errors, see what they do diff --git a/test/server/functional/level_component.spec.coffee b/test/server/functional/level_component.spec.coffee index 4269eee21..e185657ba 100644 --- a/test/server/functional/level_component.spec.coffee +++ b/test/server/functional/level_component.spec.coffee @@ -130,17 +130,17 @@ describe 'LevelComponent', -> xit ' can\'t be requested with HTTP PUT method', (done) -> request.put {uri:url+'/'+components[0]._id}, (err, res) -> - expect(res.statusCode).toBe(404) + expect(res.statusCode).toBe(405) done() it ' can\'t be requested with HTTP HEAD method', (done) -> request.head {uri:url+'/'+components[0]._id}, (err, res) -> - expect(res.statusCode).toBe(404) + expect(res.statusCode).toBe(405) done() it ' can\'t be requested with HTTP DEL method', (done) -> request.del {uri:url+'/'+components[0]._id}, (err, res) -> - expect(res.statusCode).toBe(404) + expect(res.statusCode).toBe(405) done() it 'get schema', (done) -> diff --git a/test/server/functional/level_system.spec.coffee b/test/server/functional/level_system.spec.coffee index c933eb8bb..a84c75308 100644 --- a/test/server/functional/level_system.spec.coffee +++ b/test/server/functional/level_system.spec.coffee @@ -123,12 +123,12 @@ describe 'LevelSystem', -> it ' can\'t be requested with HTTP HEAD method', (done) -> request.head {uri:url+'/'+systems[0]._id}, (err, res) -> - expect(res.statusCode).toBe(404) + expect(res.statusCode).toBe(405) done() it ' can\'t be requested with HTTP DEL method', (done) -> request.del {uri:url+'/'+systems[0]._id}, (err, res) -> - expect(res.statusCode).toBe(404) + expect(res.statusCode).toBe(405) done() it 'get schema', (done) ->