codecombat/spec/server/functional/article.spec.coffee
Scott Erickson 7bab895dee Generalize new I18N view system
Previously, when diplomats submit translations, the system
would try to figure out whether it should be a 'patch' or a 'change',
and then would either create a patch for an admin or artisan to
review and accept or reject, or would apply the changes immediately
and they would be live. This was done as a compromise between
getting translations live quickly, but also preventing already-translated
text from getting overwritten without oversight.

But having the client handle this added logical complexity. So
this makes all diplomats submit patches, no matter what. The server
is then in charge of deciding if it should auto-accept the patch or not.
Either way, a patch is created.

There was also much refactoring. This commit includes:

* Update jsondiffpatch so changes within array items are handled correctly
* Refactor posting patches to use the new auto-accepting logic, and out of Patch model
* Refactor POST /db/patch/:handle/status so that it doesn't rely on handlers
* Refactor patch stat handling to ensure auto-accepted patches are counted
* Refactor User.incrementStat to use mongodb update commands, to avoid race conditions
* Refactor Patch tests
2016-09-09 10:59:26 -07:00

728 lines
29 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: {name:['test']}
commitMessage: 'Test commit'
target: {
collection: 'article'
id: article._id
}
}}
expect(res.statusCode).toBe(201)
[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 404 for handles that do not exist', utils.wrap (done) ->
[res, body] = yield request.getAsync getURL("/db/article/dne/patches"), { json: true }
expect(res.statusCode).toBe(404)
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()
describe 'POST /db/article/:handle/patch', ->
it 'creates a new version if the changes are translation only', utils.wrap (done) ->
# create article
yield utils.clearModels([Article])
admin = yield utils.initAdmin()
yield utils.loginUser(admin)
article = yield utils.makeArticle()
# submit a translation patch
user = yield utils.initUser()
yield utils.loginUser(user)
originalArticle = article.toObject()
changedArticle = _.extend({}, originalArticle, {i18n: {de: {name: 'Name in German'}}})
json = {
delta: jsondiffpatch.diff(originalArticle, changedArticle)
commitMessage: 'Server test commit'
target: {
collection: 'article'
id: article.id
}
}
url = utils.getURL("/db/article/#{article.id}/patch")
[res, body] = yield request.postAsync({ url, json })
expect(res.statusCode).toBe(201)
[firstArticle, secondArticle] = yield Article.find().sort('_id')
expectedVersion = { isLatestMinor: false, isLatestMajor: false, minor: 0, major: 0 }
expect(_.isEqual(firstArticle.get('version'), expectedVersion)).toBe(true)
expectedVersion = { isLatestMinor: true, isLatestMajor: true, minor: 1, major: 0 }
expect(_.isEqual(secondArticle.get('version'), expectedVersion)).toBe(true)
expect(firstArticle.get('i18n.de.name')).toBe(undefined)
expect(secondArticle.get('i18n.de.name')).toBe('Name in German')
expect(firstArticle.get('creator').equals(admin._id)).toBe(true)
expect(secondArticle.get('creator').equals(user._id)).toBe(true)
expect(firstArticle.get('commitMessage')).toBeUndefined()
expect(secondArticle.get('commitMessage')).toBe('Server test commit')
done()