codecombat/spec/server/functional/article.spec.coffee
phoenixeliot c191f63c15 Revert all of my sendwithus changes
Revert "Fix more sendwithus things"

This reverts commit 9d4215d99d.

Revert "Fix some missed sendwithus changes"

This reverts commit 08bc32e005.

Revert "Fix email tests"

This reverts commit fcb2ce8504.

Revert "Use SendWithUs versioning"

This reverts commit 81d9e19221.
2016-06-06 16:54:15 -07:00

682 lines
27 KiB
CoffeeScript

require '../common'
utils = require '../utils'
_ = require 'lodash'
Promise = require 'bluebird'
request = require '../request'
requestAsync = Promise.promisify(request, {multiArgs: true})
Article = require '../../../server/models/Article'
User = require '../../../server/models/User'
describe 'GET /db/article', ->
articleData1 = { name: 'Article 1', body: 'Article 1 body cow', i18nCoverage: [] }
articleData2 = { name: 'Article 2', body: 'Article 2 body moo' }
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin()
yield utils.loginUser(@admin)
yield request.postAsync(getURL('/db/article'), { json: articleData1 })
yield request.postAsync(getURL('/db/article'), { json: articleData2 })
yield utils.becomeAnonymous()
done()
it 'returns an array of Article objects', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL('/db/article'), json: true }
expect(body.length).toBe(2)
done()
it 'accepts a limit parameter', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?limit=1'), json: true}
expect(body.length).toBe(1)
done()
it 'returns 422 for an invalid limit parameter', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?limit=word'), json: true}
expect(res.statusCode).toBe(422)
done()
it 'accepts a skip parameter', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?skip=1'), json: true}
expect(body.length).toBe(1)
[res, body] = yield request.getAsync {uri: getURL('/db/article?skip=2'), json: true}
expect(body.length).toBe(0)
done()
it 'returns 422 for an invalid skip parameter', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?skip=???'), json: true}
expect(res.statusCode).toBe(422)
done()
it 'accepts a custom project parameter', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?project=name,body'), json: true}
expect(body.length).toBe(2)
for doc in body
expect(_.size(_.xor(_.keys(doc), ['_id', 'name', 'body']))).toBe(0)
done()
it 'returns a default projection if project is "true"', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?project=true'), json: true}
expect(res.statusCode).toBe(200)
expect(body.length).toBe(2)
expect(body[0].body).toBeUndefined()
expect(body[0].version).toBeDefined()
done()
it 'accepts custom filter parameters', utils.wrap (done) ->
yield utils.loginUser(@admin)
[res, body] = yield request.getAsync {uri: getURL('/db/article?filter[slug]="article-1"'), json: true}
expect(body.length).toBe(1)
done()
it 'ignores custom filter parameters for non-admins', utils.wrap (done) ->
user = yield utils.initUser()
yield utils.loginUser(user)
[res, body] = yield request.getAsync {uri: getURL('/db/article?filter[slug]="article-1"'), json: true}
expect(body.length).toBe(2)
done()
it 'accepts custom condition parameters', utils.wrap (done) ->
yield utils.loginUser(@admin)
[res, body] = yield request.getAsync {uri: getURL('/db/article?conditions[select]="slug body"'), json: true}
expect(body.length).toBe(2)
for doc in body
expect(_.size(_.xor(_.keys(doc), ['_id', 'slug', 'body']))).toBe(0)
done()
it 'ignores custom condition parameters for non-admins', utils.wrap (done) ->
user = yield utils.initUser()
yield utils.loginUser(user)
[res, body] = yield request.getAsync {uri: getURL('/db/article?conditions[select]="slug body"'), json: true}
expect(body.length).toBe(2)
for doc in body
expect(doc.name).toBeDefined()
done()
it 'allows non-admins to view by i18n-coverage', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?view=i18n-coverage'), json: true}
expect(body.length).toBe(1)
expect(body[0].slug).toBe('article-1')
done()
it 'allows non-admins to search by text', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL('/db/article?term=moo'), json: true}
expect(body.length).toBe(1)
expect(body[0].slug).toBe('article-2')
done()
describe 'POST /db/article', ->
articleData = { name: 'Article', body: 'Article', otherProp: 'not getting set' }
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin({})
yield utils.loginUser(@admin)
[@res, @body] = yield request.postAsync {
uri: getURL('/db/article'), json: articleData
}
done()
it 'creates a new Article, returning 201', utils.wrap (done) ->
expect(@res.statusCode).toBe(201)
article = yield Article.findById(@body._id).exec()
expect(article).toBeDefined()
done()
it 'sets creator to the user who created it', ->
expect(@res.body.creator).toBe(@admin.id)
it 'sets original to _id', ->
body = @res.body
expect(body.original).toBe(body._id)
it 'returns 422 when no input is provided', utils.wrap (done) ->
[res, body] = yield request.postAsync { uri: getURL('/db/article') }
expect(res.statusCode).toBe(422)
done()
it 'allows you to set Article\'s editableProperties', ->
expect(@body.name).toBe('Article')
it 'ignores properties not included in editableProperties', ->
expect(@body.otherProp).toBeUndefined()
it 'returns 422 when properties do not pass validation', utils.wrap (done) ->
[res, body] = yield request.postAsync {
uri: getURL('/db/article'), json: { i18nCoverage: 9001 }
}
expect(res.statusCode).toBe(422)
expect(body.validationErrors).toBeDefined()
done()
it 'allows admins to create Articles', -> # handled in beforeEach
it 'allows artisans to create Articles', utils.wrap (done) ->
yield utils.clearModels([Article])
artisan = yield utils.initArtisan({})
yield utils.loginUser(artisan)
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
expect(res.statusCode).toBe(201)
done()
it 'does not allow normal users to create Articles', utils.wrap (done) ->
yield utils.clearModels([Article])
user = yield utils.initUser({})
yield utils.loginUser(user)
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
expect(res.statusCode).toBe(403)
done()
it 'does not allow anonymous users to create Articles', utils.wrap (done) ->
yield utils.clearModels([Article])
yield utils.becomeAnonymous()
[res, body] = yield request.postAsync({uri: getURL('/db/article'), json: articleData })
expect(res.statusCode).toBe(401)
done()
it 'does not allow creating Articles with reserved words', utils.wrap (done) ->
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: { name: 'Names' } }
expect(res.statusCode).toBe(422)
done()
it 'does not allow creating a second article of the same name', utils.wrap (done) ->
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(409)
done()
describe 'GET /db/article/:handle', ->
articleData = { name: 'Some Name', body: 'Article' }
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin({})
yield utils.loginUser(@admin)
[@res, @body] = yield request.postAsync {
uri: getURL('/db/article'), json: articleData
}
done()
it 'returns Article by id', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL("/db/article/#{@body._id}"), json: true}
expect(res.statusCode).toBe(200)
expect(_.isObject(body)).toBe(true)
done()
it 'returns Article by slug', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL("/db/article/some-name"), json: true}
expect(res.statusCode).toBe(200)
expect(_.isObject(body)).toBe(true)
done()
it 'returns not found if handle does not exist in the db', utils.wrap (done) ->
[res, body] = yield request.getAsync {uri: getURL("/db/article/dne"), json: true}
expect(res.statusCode).toBe(404)
done()
putTests = (method='PUT') ->
articleData = { name: 'Some Name', body: 'Article' }
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin({})
yield utils.loginUser(@admin)
[@res, @body] = yield request.postAsync {
uri: getURL('/db/article'), json: articleData
}
done()
it 'edits editable Article properties', utils.wrap (done) ->
[res, body] = yield requestAsync {method: method, uri: getURL("/db/article/#{@body._id}"), json: { body: 'New body' }}
expect(body.body).toBe('New body')
done()
it 'updates the slug when the name is changed', utils.wrap (done) ->
[res, body] = yield requestAsync {method: method, uri: getURL("/db/article/#{@body._id}"), json: json = { name: 'New name' }}
expect(body.name).toBe('New name')
expect(body.slug).toBe('new-name')
done()
it 'does not allow normal artisan, non-admins to make changes', utils.wrap (done) ->
artisan = yield utils.initArtisan({})
yield utils.loginUser(artisan)
[res, body] = yield requestAsync {method: method, uri: getURL("/db/article/#{@body._id}"), json: { name: 'Another name' }}
expect(res.statusCode).toBe(403)
done()
describe 'PUT /db/article/:handle', -> putTests('PUT')
describe 'PATCH /db/article/:handle', -> putTests('PATCH')
describe 'POST /db/article/:handle/new-version', ->
articleData = { name: 'Article name', body: 'Article body', i18n: {} }
articleID = null
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin({})
yield utils.loginUser(@admin)
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
articleID = body._id
done()
postNewVersion = Promise.promisify (json, expectedStatus=201, done) ->
if _.isFunction(expectedStatus)
done = expectedStatus
expectedStatus = 201
url = getURL("/db/article/#{articleID}/new-version")
request.post { uri: url, json: json }, (err, res, body) ->
expect(res.statusCode).toBe(expectedStatus)
done(err)
testArrayEqual = (given, expected) ->
expect(_.isEqual(given, expected)).toBe(true)
it 'creates a new major version, updating model and version properties', utils.wrap (done) ->
yield postNewVersion({ name: 'Article name', body: 'New body' })
yield postNewVersion({ name: 'New name', body: 'New new body' })
articles = yield Article.find()
expect(articles.length).toBe(3)
versions = (article.get('version') for article in articles)
articles = (article.toObject() for article in articles)
testArrayEqual(_.pluck(versions, 'major'), [0, 1, 2])
testArrayEqual(_.pluck(versions, 'minor'), [0, 0, 0])
testArrayEqual(_.pluck(versions, 'isLatestMajor'), [false, false, true])
testArrayEqual(_.pluck(versions, 'isLatestMinor'), [true, true, true])
testArrayEqual(_.pluck(articles, 'name'), ['Article name', 'Article name', 'New name'])
testArrayEqual(_.pluck(articles, 'body'), ['Article body', 'New body', 'New new body'])
testArrayEqual(_.pluck(articles, 'slug'), [undefined, undefined, 'new-name'])
testArrayEqual(_.pluck(articles, 'index'), [undefined, undefined, true])
done()
it 'works if there is no document with the appropriate version settings (new major)', utils.wrap (done) ->
article = yield Article.findById(articleID)
article.set({ 'version.isLatestMajor': false, 'version.isLatestMinor': false })
yield article.save()
yield postNewVersion({ name: 'Article name', body: 'New body' })
articles = yield Article.find()
expect(articles.length).toBe(2)
versions = (article.get('version') for article in articles)
articles = (article.toObject() for article in articles)
testArrayEqual(_.pluck(versions, 'major'), [0, 1])
testArrayEqual(_.pluck(versions, 'minor'), [0, 0])
testArrayEqual(_.pluck(versions, 'isLatestMajor'), [false, true])
testArrayEqual(_.pluck(versions, 'isLatestMinor'), [false, true]) # does not fix the old version's value
testArrayEqual(_.pluck(articles, 'body'), ['Article body', 'New body'])
testArrayEqual(_.pluck(articles, 'slug'), [undefined, 'article-name'])
testArrayEqual(_.pluck(articles, 'index'), [undefined, true])
done()
it 'creates a new minor version if version.major is included', utils.wrap (done) ->
yield postNewVersion({ name: 'Article name', body: 'New body', version: { major: 0 } })
yield postNewVersion({ name: 'Article name', body: 'New new body', version: { major: 0 } })
articles = yield Article.find()
expect(articles.length).toBe(3)
versions = (article.get('version') for article in articles)
articles = (article.toObject() for article in articles)
testArrayEqual(_.pluck(versions, 'major'), [0, 0, 0])
testArrayEqual(_.pluck(versions, 'minor'), [0, 1, 2])
testArrayEqual(_.pluck(versions, 'isLatestMajor'), [false, false, true])
testArrayEqual(_.pluck(versions, 'isLatestMinor'), [false, false, true])
testArrayEqual(_.pluck(articles, 'name'), ['Article name', 'Article name', 'Article name'])
testArrayEqual(_.pluck(articles, 'body'), ['Article body', 'New body', 'New new body'])
testArrayEqual(_.pluck(articles, 'slug'), [undefined, undefined, 'article-name'])
testArrayEqual(_.pluck(articles, 'index'), [undefined, undefined, true])
done()
it 'works if there is no document with the appropriate version settings (new minor)', utils.wrap (done) ->
article = yield Article.findById(articleID)
article.set({ 'version.isLatestMajor': false, 'version.isLatestMinor': false })
yield article.save()
yield postNewVersion({ name: 'Article name', body: 'New body', version: { major: 0 } })
articles = yield Article.find()
expect(articles.length).toBe(2)
versions = (article.get('version') for article in articles)
articles = (article.toObject() for article in articles)
testArrayEqual(_.pluck(versions, 'major'), [0, 0])
testArrayEqual(_.pluck(versions, 'minor'), [0, 1])
testArrayEqual(_.pluck(versions, 'isLatestMajor'), [false, false])
testArrayEqual(_.pluck(versions, 'isLatestMinor'), [false, true])
testArrayEqual(_.pluck(articles, 'body'), ['Article body', 'New body'])
testArrayEqual(_.pluck(articles, 'slug'), [undefined, 'article-name'])
testArrayEqual(_.pluck(articles, 'index'), [undefined, true])
done()
it 'allows adding new minor versions to old major versions', utils.wrap (done) ->
yield postNewVersion({ name: 'Article name', body: 'New body' })
yield postNewVersion({ name: 'Article name', body: 'New new body', version: { major: 0 } })
articles = yield Article.find()
expect(articles.length).toBe(3)
versions = (article.get('version') for article in articles)
articles = (article.toObject() for article in articles)
testArrayEqual(_.pluck(versions, 'major'), [0, 1, 0])
testArrayEqual(_.pluck(versions, 'minor'), [0, 0, 1])
testArrayEqual(_.pluck(versions, 'isLatestMajor'), [false, true, false])
testArrayEqual(_.pluck(versions, 'isLatestMinor'), [false, true, true])
testArrayEqual(_.pluck(articles, 'name'), ['Article name', 'Article name', 'Article name'])
testArrayEqual(_.pluck(articles, 'body'), ['Article body', 'New body', 'New new body'])
testArrayEqual(_.pluck(articles, 'slug'), [undefined, 'article-name', undefined])
testArrayEqual(_.pluck(articles, 'index'), [undefined, true, undefined])
done()
it 'unsets properties which are not included in the request', utils.wrap (done) ->
yield postNewVersion({ name: 'Article name', version: { major: 0 } })
articles = yield Article.find()
expect(articles.length).toBe(2)
expect(articles[1].get('body')).toBeUndefined()
done()
it 'works for artisans', utils.wrap (done) ->
yield utils.logout()
artisan = yield utils.initArtisan()
yield utils.loginUser(artisan)
yield postNewVersion({ name: 'Article name', body: 'New body' })
articles = yield Article.find()
expect(articles.length).toBe(2)
done()
it 'works for normal users submitting translations', utils.wrap (done) ->
yield utils.logout()
user = yield utils.initUser()
yield utils.loginUser(user)
yield postNewVersion({ name: 'Article name', body: 'Article body', i18n: { fr: { name: 'Le Article' }}}, 201)
articles = yield Article.find()
expect(articles.length).toBe(2)
done()
it 'does not work for normal users', utils.wrap (done) ->
yield utils.logout()
user = yield utils.initUser()
yield utils.loginUser(user)
yield postNewVersion({ name: 'Article name', body: 'New body' }, 403)
articles = yield Article.find()
expect(articles.length).toBe(1)
done()
it 'does not work for anonymous users', utils.wrap (done) ->
yield utils.becomeAnonymous()
yield postNewVersion({ name: 'Article name', body: 'New body' }, 401)
articles = yield Article.find()
expect(articles.length).toBe(1)
done()
it 'notifies watchers of changes', utils.wrap (done) ->
sendwithus = require '../../../server/sendwithus'
spyOn(sendwithus.api, 'send').and.callFake (context, cb) ->
expect(context.email_id).toBe(sendwithus.templates.change_made_notify_watcher)
expect(context.recipient.address).toBe('test@gmail.com')
done()
user = yield User({email: 'test@gmail.com', name: 'a user'}).save()
article = yield Article.findById(articleID)
article.set('watchers', article.get('watchers').concat([user.get('_id')]))
yield article.save()
yield postNewVersion({ name: 'Article name', body: 'New body', commitMessage: 'Commit message' })
it 'sends a notification to artisan and main Slack channels', utils.wrap (done) ->
slack = require '../../../server/slack'
spyOn(slack, 'sendSlackMessage')
yield postNewVersion({ name: 'Article name', body: 'New body' })
expect(slack.sendSlackMessage).toHaveBeenCalled()
done()
describe 'version fetching endpoints', ->
articleData = { name: 'Original version', body: 'Article body' }
articleOriginal = null
postNewVersion = Promise.promisify (json, expectedStatus=201, done) ->
if _.isFunction(expectedStatus)
done = expectedStatus
expectedStatus = 201
url = getURL("/db/article/#{articleOriginal}/new-version")
request.post { uri: url, json: json }, (err, res) ->
expect(res.statusCode).toBe(expectedStatus)
done(err)
beforeEach utils.wrap (done) ->
yield utils.clearModels([Article])
@admin = yield utils.initAdmin({})
yield utils.loginUser(@admin)
[res, body] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
articleOriginal = body._id
yield postNewVersion({ name: 'Latest minor version', body: 'New body', version: {major: 0} })
yield postNewVersion({ name: 'Latest major version', body: 'New new body' })
done()
describe 'GET /db/article/:handle/version/:version', ->
it 'returns the latest version for the given original article when :version is empty', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL("/db/article/#{articleOriginal}/version"), json: true }
expect(body.name).toBe('Latest major version')
done()
it 'returns the latest of a given major version when :version is X', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL("/db/article/#{articleOriginal}/version/0"), json: true }
expect(body.name).toBe('Latest minor version')
done()
it 'returns a specific version when :version is X.Y', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL("/db/article/#{articleOriginal}/version/0.0"), json: true }
expect(body.name).toBe('Original version')
done()
it 'returns 422 when the original value is invalid', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL('/db/article/dne/version'), json: true }
expect(res.statusCode).toBe(422)
done()
it 'returns 404 when the original value cannot be found', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL('/db/article/012345678901234567890123/version'), json: true }
expect(res.statusCode).toBe(404)
done()
describe 'GET /db/article/:handle/versions', ->
it 'returns an array of versions sorted by creation for the given original article', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL("/db/article/#{articleOriginal}/versions"), json: true }
expect(body.length).toBe(3)
expect(body[0].name).toBe('Latest major version')
expect(body[1].name).toBe('Latest minor version')
expect(body[2].name).toBe('Original version')
done()
it 'projects most properties by default', utils.wrap (done) ->
[res, body] = yield request.getAsync { uri: getURL("/db/article/#{articleOriginal}/versions"), json: true }
expect(body[0].body).toBeUndefined()
done()
describe 'GET /db/article/:handle/files', ->
it 'returns an array of file metadata for the given original article', utils.wrap (done) ->
yield utils.clearModels([Article])
articleData = { name: 'Article', body: 'Article' }
admin = yield utils.initAdmin({})
yield utils.loginUser(admin)
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
[res, body] = yield request.postAsync(getURL('/file'), { json: {
url: getURL('/assets/main.html')
filename: 'test.html'
path: 'db/article/'+article.original
mimetype: 'text/html'
}})
[res, body] = yield request.getAsync(getURL('/db/article/'+article.original+'/files'), {json: true})
expect(body.length).toBe(1)
expect(body[0].filename).toBe('test.html')
expect(body[0].metadata.path).toBe('db/article/'+article.original)
done()
describe 'GET and POST /db/article/:handle/names', ->
articleData1 = { name: 'Article 1', body: 'Article 1 body' }
articleData2 = { name: 'Article 2', body: 'Article 2 body' }
it 'returns an object mapping ids to names', utils.wrap (done) ->
yield utils.clearModels([Article])
admin = yield utils.initAdmin({})
yield utils.loginUser(admin)
[res, article1] = yield request.postAsync(getURL('/db/article'), { json: articleData1 })
[res, article2] = yield request.postAsync(getURL('/db/article'), { json: articleData2 })
yield utils.becomeAnonymous()
[res, body] = yield request.getAsync { uri: getURL('/db/article/names?ids='+[article1._id, article2._id].join(',')), json: true }
expect(body.length).toBe(2)
expect(body[0].name).toBe('Article 1')
[res, body] = yield request.postAsync { uri: getURL('/db/article/names?ids='+[article1._id, article2._id].join(',')), json: true }
expect(body.length).toBe(2)
expect(body[0].name).toBe('Article 1')
done()
describe 'GET /db/article/:handle/patches', ->
it 'returns pending patches for the given original article', utils.wrap (done) ->
yield utils.clearModels([Article])
articleData = { name: 'Article', body: 'Article' }
admin = yield utils.initAdmin({})
yield utils.loginUser(admin)
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
[res, patch] = yield request.postAsync { uri: getURL('/db/patch'), json: {
delta: []
commitMessage: 'Test commit'
target: {
collection: 'article'
id: article._id
}
}}
[res, patches] = yield request.getAsync getURL("/db/article/#{article._id}/patches"), { json: true }
expect(res.statusCode).toBe(200)
expect(patches.length).toBe(1)
expect(patches[0]._id).toBe(patch._id)
done()
it 'returns 422 for invalid object ids', utils.wrap (done) ->
[res, body] = yield request.getAsync getURL("/db/article/invalid/patches"), { json: true }
expect(res.statusCode).toBe(422)
done()
describe 'POST /db/article/:handle/watchers', ->
it 'adds self to the list of watchers, and is idempotent', utils.wrap (done) ->
# create article
yield utils.clearModels([Article])
articleData = { name: 'Article', body: 'Article' }
admin = yield utils.initAdmin({})
yield utils.loginUser(admin)
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
# add new user as watcher
yield utils.logout()
user = yield utils.initUser()
yield utils.loginUser(user)
[res, article] = yield request.postAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
expect(res.statusCode).toBe(200)
expect(_.contains(article.watchers, user.id)).toBe(true)
# check idempotence, db
numWatchers = article.watchers.length
[res, article] = yield request.postAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
expect(res.statusCode).toBe(200)
expect(numWatchers).toBe(article.watchers.length)
article = yield Article.findById(article._id)
expect(_.last(article.get('watchers')).toString()).toBe(user.id)
done()
describe 'DELETE /db/article/:handle/watchers', ->
it 'removes self from the list of watchers, and is idempotent', utils.wrap (done) ->
# create article
yield utils.clearModels([Article])
articleData = { name: 'Article', body: 'Article' }
admin = yield utils.initAdmin({})
yield utils.loginUser(admin)
[res, article] = yield request.postAsync { uri: getURL('/db/article'), json: articleData }
expect(res.statusCode).toBe(201)
# add new user as watcher
yield utils.logout()
user = yield utils.initUser()
yield utils.loginUser(user)
[res, article] = yield request.postAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
expect(_.contains(article.watchers, user.id)).toBe(true)
# remove user as watcher
[res, article] = yield request.delAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
expect(res.statusCode).toBe(200)
expect(_.contains(article.watchers, user.id)).toBe(false)
# check idempotence, db
numWatchers = article.watchers.length
[res, article] = yield request.delAsync { uri: getURL("/db/article/#{article._id}/watchers"), json: true }
expect(res.statusCode).toBe(200)
expect(numWatchers).toBe(article.watchers.length)
article = yield Article.findById(article._id)
ids = (id.toString() for id in article.get('watchers'))
expect(_.contains(ids, user.id)).toBe(false)
done()