@rill/mongoose
Version:
Resource based crud api generator for Rill and Mongoose.
427 lines (380 loc) • 11.9 kB
JavaScript
const _ = require('lodash')
const assert = require('assert')
const rill = require('rill')
const mongoose = require('mongoose')
const agent = require('supertest')
const resource = require('../lib')
const DATA = require('./data.json')
const UNUSED_ID = '000000000000000000000000'
const VISIBLE = ['_id', 'name', 'test', 'related']
const Model = mongoose.model('test', {
name: { type: String, required: true },
test: { type: Boolean },
hidden: { type: String, hidden: true },
related: { type: mongoose.Schema.ObjectId, ref: 'test' }
})
describe('@rill/mongoose', () => {
before(() => mongoose.connect('mongodb://localhost/rill-mongoose-test'))
after(() => mongoose.disconnect())
// Reset database.
beforeEach(() => {
return Model
.remove()
.then(() => {
return _.reduce(_.clone(DATA), (p, doc) => p.then(() => {
return Model.create(doc)
}), Promise.resolve())
})
})
describe('GET', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, { find: 'default', findById: 'default' }))
.listen()
.unref())
it('should get all', () => {
return request
.get('/test')
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.map(DATA, _.partial(_.pick, _, VISIBLE))
assert.deepEqual(res.body, expected, 'GET all documents')
})
})
it('should search and find one doc', () => {
return request
.get('/test')
.query({ name: DATA[0].name })
.expect(200)
.expect('x-total-count', '1')
.then((res) => {
const expected = [_.pick(DATA[0], VISIBLE)]
assert.deepEqual(res.body, expected, 'GET search documents')
})
})
it('should search and find no docs', () => {
return request
.get('/test')
.query({ name: 'no such thing' })
.expect(200)
.expect('x-total-count', '0')
})
it('should limit to 5 docs', () => {
return request
.get('/test')
.query({ $limit: 5 })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.take(_.map(DATA, _.partial(_.pick, _, VISIBLE)), 5)
assert.deepEqual(res.body, expected, 'GET limited documents')
})
})
it('should skip 5 docs', () => {
return request
.get('/test')
.query({ $skip: 5 })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.drop(_.map(DATA, _.partial(_.pick, _, VISIBLE)), 5)
assert.deepEqual(res.body, expected, 'GET skipped documents')
})
})
it('should sort docs asc', () => {
return request
.get('/test')
.query({ $sort: 'name' })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.sortBy(_.map(DATA, _.partial(_.pick, _, VISIBLE)), 'name')
assert.deepEqual(res.body, expected, 'GET sorted asc documents')
})
})
it('should sort docs desc', () => {
return request
.get('/test')
.query({ $sort: '-name' })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.reverse(_.sortBy(_.map(DATA, _.partial(_.pick, _, VISIBLE)), 'name'))
assert.deepEqual(res.body, expected, 'GET sorted desc documents')
})
})
it('should sort multiple fields', () => {
return request
.get('/test')
.query({ $sort: 'test name' })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.sortBy(_.map(DATA, _.partial(_.pick, _, VISIBLE)), ['test', 'name'])
assert.deepEqual(res.body, expected, 'GET sorted (name, test) documents')
})
})
it('should select fields', () => {
return request
.get('/test')
.query({ $select: '-_id name' })
.expect(200)
.expect('x-total-count', '10')
.then((res) => {
const expected = _.map(DATA, _.partial(_.pick, _, 'name'))
assert.deepEqual(res.body, expected, 'GET select (name) documents')
})
})
it('should populate fields', () => {
return request
.get('/test/' + DATA[0]._id)
.query({ $populate: 'related[name]' })
.expect(200)
.expect('x-total-count', '1')
.then((res) => {
const expected = _.pick(DATA[0], VISIBLE)
expected.related = _.pick(expected, ['name', '_id'])
assert.deepEqual(res.body, expected, 'GET populate/select (name) documents')
})
})
it('should populate nested fields', () => {
return request
.get('/test/' + DATA[0]._id)
.query({ $populate: 'related:related[name]' })
.expect(200)
.expect('x-total-count', '1')
.then((res) => {
const expected = _.assign(_.pick(DATA[0], VISIBLE), {
related: _.assign(_.pick(DATA[0], VISIBLE), {
related: _.pick(DATA[0], ['name', '_id'])
})
})
assert.deepEqual(res.body, expected, 'GET populate-deep/select (name) documents')
})
})
it('should find a doc by id', () => {
return request
.get('/test/' + DATA[0]._id)
.expect(200)
.expect('x-total-count', '1')
})
it('should not find a doc when id doesnt exist', () => {
return request
.get('/test/' + UNUSED_ID)
.expect(204)
.expect('x-total-count', '0')
})
})
describe('POST', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, { create: 'default' }))
.listen()
.unref())
const validDoc = {
name: 'Dylan Piercey',
test: true,
hidden: 'Wow wow wow, what are you doing?',
extra: 'Get outa here!'
}
it('should add a new doc', () => {
return request
.post('/test')
.send(validDoc)
.expect(201)
.then((res) => {
const actual = _.omit(res.body, '_id')
const expected = _.omit(validDoc, ['hidden', 'extra'])
assert.deepEqual(actual, expected, 'POST document')
// Make sure hidden didn't make it into the database.
return Model.findById(res.body._id).then((doc) => {
assert.equal(doc.hidden, undefined, 'POST hidden field')
assert.equal(doc.extra, undefined, 'POST extra field')
})
})
})
it('should throw a validation error', () => {
return request
.post('/test')
.send(_.assign(_.clone(validDoc), { name: null }))
.expect(400)
.expect('X-Error-Message', 'Path `name` is required.')
})
})
describe('PUT', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, { create: 'default', save: 'default' }))
.listen()
.unref())
const validDoc = {
name: 'Dylan Piercey',
test: true
}
const putDoc = {
name: 'New Name'
}
// Create test doc.
const createTestDoc = () => request
.post('/test')
.send(validDoc)
.expect(201)
.then((res) => res.body)
it('should update doc', () => {
return createTestDoc().then(testDoc => {
return request
.put('/test/' + testDoc._id)
.send(putDoc)
.expect(200)
.then((res) => {
const actual = _.omit(res.body, '_id')
assert.deepEqual(actual, putDoc, 'PUT document')
})
})
})
it('should throw validation error', () => {
return createTestDoc().then(testDoc => {
return request
.put('/test/' + testDoc._id)
.send(_.assign(_.clone(validDoc), { name: null }))
.expect(400)
.expect('X-Error-Message', 'Path `name` is required.')
})
})
it('should fail when document missing', () => {
return request
.put('/test/' + UNUSED_ID)
.send(_.clone(validDoc))
.expect(204)
})
})
describe('PATCH', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, { create: 'default', save: 'default' }))
.listen()
.unref())
const validDoc = {
name: 'Dylan Piercey',
test: true
}
const patchDoc = {
name: 'New Name'
}
// Create test doc.
const createTestDoc = () => request
.post('/test')
.send(validDoc)
.expect(201)
.then((res) => res.body)
it('should update doc', () => {
return createTestDoc().then(testDoc => {
return request
.patch('/test/' + testDoc._id)
.send(patchDoc)
.expect(200)
.then((res) => {
const actual = _.omit(res.body, '_id')
const expected = _.assign(_.clone(validDoc), patchDoc)
assert.deepEqual(actual, expected, 'PATCH document')
})
})
})
it('should throw validation error', () => {
return createTestDoc().then(testDoc => {
return request
.patch('/test/' + testDoc._id)
.send(_.assign(_.clone(validDoc), { name: null }))
.expect(400)
.expect('X-Error-Message', 'Path `name` is required.')
})
})
it('should fail when document missing', () => {
return request
.patch('/test/' + UNUSED_ID)
.expect(204)
})
})
describe('DELETE', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, { remove: 'default' }))
.listen()
.unref())
it('should delete doc', () => {
return request
.delete('/test/' + DATA[0]._id)
.expect(200)
.then((res) => {
Model.count().then((count) => {
assert.equal(count, _.size(DATA) - 1, 'DELETE one document')
})
})
})
it('should fail when document missing', () => {
return request
.delete('/test/' + UNUSED_ID)
.expect(204)
})
})
describe('OVERRIDES', () => {
const request = agent(rill()
.use(require('@rill/body')())
.use(resource(Model, {
find: respondWith(200, 'find'),
findById: respondWith(200, 'findById'),
create: respondWith(200, 'create'),
save: respondWith(200, 'save'),
remove: respondWith(200, 'remove')
}))
.listen()
.unref())
function respondWith (status, message) {
return (ctx) => {
ctx.res.status = 200
ctx.res.body = { message: message }
}
}
it('should override GET', () => {
return request
.get('/test')
.expect(200)
.then((res) => {
assert.deepEqual(res.body, { message: 'find' }, 'OVERRIDE GET')
})
})
it('should override POST', () => {
return request
.post('/test')
.expect(200)
.then((res) => {
assert.deepEqual(res.body, { message: 'create' }, 'OVERRIDE POST')
})
})
it('should override PUT', () => {
return request
.put('/test/1')
.expect(200)
.then((res) => {
assert.deepEqual(res.body, { message: 'save' }, 'OVERRIDE PUT')
})
})
it('should override PATCH', () => {
return request
.patch('/test/1')
.expect(200)
.then((res) => {
assert.deepEqual(res.body, { message: 'save' }, 'OVERRIDE PATCH')
})
})
it('should override DELETE', () => {
return request
.delete('/test/1')
.expect(200)
.then((res) => {
assert.deepEqual(res.body, { message: 'remove' }, 'OVERRIDE DELETE')
})
})
})
})