Quellcode durchsuchen

implemented more async functionality

Tomi Cvetic vor 8 Jahren
Ursprung
Commit
b01b786be3
17 geänderte Dateien mit 399 neuen und 284 gelöschten Zeilen
  1. 0 0
      .eslintrc
  2. 14 1
      _samples.js
  3. 5 6
      core/basicSchema.js
  4. 2 2
      index.js
  5. 6 0
      jsdoc.json
  6. 3 1
      package.json
  7. 0 47
      project/__mocks__/model.js
  8. 0 19
      project/_sampleData.js
  9. 0 105
      project/route.js
  10. 64 0
      projects/__mocks__/model.js
  11. 142 0
      projects/_sampleData.js
  12. 0 0
      projects/model.js
  13. 14 0
      projects/route.js
  14. 8 15
      projects/route.test.js
  15. 54 31
      routeGen.js
  16. 65 57
      routeGen.test.js
  17. 22 0
      routeGenModel.js

+ 0 - 0
.eslintrc


+ 14 - 1
_samples.js

@@ -18,7 +18,15 @@ export const model = [{
     type: 'commercial',
     salery: 150000,
     fun: true
-  }]
+  }],
+  __history: {
+    author: '58d3d6883d34293254afae42',
+    created: new Date(),
+    tag: '',
+    reference: [],
+    head: true,
+    comment: 'Brazilian model.'
+  }
 }, {
   _id: '2d3ad6883d34293254afae42',
   name: 'Adriana',
@@ -59,6 +67,11 @@ export const model = [{
   }]
 }]
 
+export const newModel = {
+  name: 'Monique',
+  description: 'Nice model'
+}
+
 export const agency = [{
   _id: '4b00d6883d34293254afae42',
   name: 'Agency 1',

+ 5 - 6
core/basicSchema.js

@@ -9,16 +9,15 @@
 */
 import mongoose from 'mongoose'
 
-const historySchema = new mongoose.Schema({
-  author: [mongoose.Schema.Types.ObjectId],
+export const historySchema = new mongoose.Schema({
+  author: { type: String, required: true },
   created: Date,
   tag: String,
-  reference: [mongoose.Schema.Types.ObjectId],
-  head: Boolean
+  reference: { type: String, required: true },
+  head: { type: Boolean, required: true },
+  comment: String
 })
 
-export const History = mongoose.model('History', historySchema)
-
 const basicSchema = {
   name: {
     type: String,

+ 2 - 2
index.js

@@ -5,7 +5,7 @@ const http = require('http')
 const mongoose = require('mongoose')
 
 /** Load the submodules */
-import project from './project/route'
+import projects from './projects/route'
 
 /** Create the express app */
 const app = express()
@@ -46,7 +46,7 @@ app.use(app.oauth.errorHandler())
 */
 
 app.get('/', welcomeRouter)
-app.use('/project', project)
+app.use('/projects', projects)
 app.use(/.*/, errorRouter)
 
 server.listen(process.env.PORT || 4100)

+ 6 - 0
jsdoc.json

@@ -0,0 +1,6 @@
+{
+    "plugins": [
+    	"plugins/markdown",
+    	"plugins/summarize"
+    ]
+}

+ 3 - 1
package.json

@@ -5,7 +5,8 @@
   "main": "index.js",
   "scripts": {
     "start": "babel-node index.js",
-    "test": "jest --watch --env=jsdom ."
+    "test": "jest --watch --env=jsdom .",
+    "doc": "jsdoc -c jsdoc.json -d code_docu -r ."
   },
   "author": "Tomi Cvetic",
   "license": "GPL-3.0",
@@ -19,6 +20,7 @@
     "dotenv": "^4.0.0",
     "express": "^4.15.2",
     "jest-cli": "^19.0.2",
+    "jsdoc": "3.4.3",
     "mongoose": "^4.9.1",
     "node-babel": "^0.1.2",
     "oauth2-server": "^2.4.1",

+ 0 - 47
project/__mocks__/model.js

@@ -1,47 +0,0 @@
-import samples from '../_sampleData.js'
-
-class ProjectMock {
-  constructor (data) {
-    for (let prop in data) {
-      this[prop] = data[prop]
-    }
-    // use this switch to simulate save errors.
-    this._saveError = false
-    // use this switch to simulate remove errors.
-    this._removeError = false
-  }
-
-  static findOne (criteria, callback) {
-    const { id } = criteria
-    // Find only the entry with id 58d3d6883d34293254afae42
-    if (id === '58d3d6883d34293254afae42') {
-      callback(undefined, samples[0])
-      return
-    }
-    // else return 404
-    callback(Error('item not found'), undefined)
-  }
-
-  static find (criteria, options, callback) {
-    const { limit, offset } = options
-    // apply criteria
-    const sampleSlice = samples.slice(offset, offset + limit)
-    // return the array
-    if (sampleSlice) {
-      callback(undefined, sampleSlice)
-      return
-    }
-    // if nothing can be shown, return 404
-    callback(Error('no items found'), undefined)
-  }
-
-  save (callback) {
-    //
-  }
-
-  remove (callback) {
-    //
-  }
-}
-
-export default ProjectMock

+ 0 - 19
project/_sampleData.js

@@ -1,19 +0,0 @@
-const projects = [{
-  name: 'Jungfrau',
-  tag: 'JU_CSD',
-  description: '9th generation GNSS receiver'
-}, {
-  name: 'Dom',
-  tag: 'DO_CSD',
-  description: '8th generation GNSS receiver'
-}, {
-  name: 'Titlis',
-  tag: 'TI_CSD',
-  description: '7th generation GNSS receiver'
-}, {
-  name: 'G6',
-  tag: 'G6',
-  description: 'Carmines favourite flavor'
-}]
-
-export default projects

+ 0 - 105
project/route.js

@@ -1,105 +0,0 @@
-import Project from './model'
-import mongoose from 'mongoose'
-import express from 'express'
-
-export function getHandler (req, res) {
-  /** GET handler (request one or more projects) */
-  let id = req.params['0']
-
-  /** check whether an id was specified. */
-  if (typeof id !== 'undefined') {
-    try {
-      id = mongoose.Types.ObjectId(id)
-    } catch (err) {
-      res.status(422)
-      res.send(err)
-      return
-    }
-    /** if yes, return the one project */
-    Project.findOne({ _id: id })
-      .exec((err, project) => {
-        if (err) {
-          res.status(404)
-          res.send(err)
-          return
-        }
-        res.json(project)
-      })
-  } else {
-    /** if not, return all projects */
-    const { limit, offset } = req.query
-    Project.find()
-      .limit(Math.min(Math.max(parseInt(limit, 10) || 20, 1), 100))
-      .skip(Math.max(parseInt(offset, 10) || 0, 0))
-      .exec((err, projects) => {
-        if (err) {
-          res.status(404)
-          res.send(err)
-          return
-        }
-        res.json(projects)
-      })
-  }
-}
-
-export function postHandler (req, res) {
-  /** POST handler (insert new projects into database) */
-  console.log(req.body)
-  const project = new Project(req.body)
-
-  project.save(function (err) {
-    if (err) {
-      res.status(422)
-      res.send(err)
-      return
-    }
-    res.send()
-  })
-}
-
-export function putHandler (req, res) {
-  /** PUT handler (update existing project) */
-  const id = mongoose.Types.ObjectId(req.params['0'])
-
-  Project.findOne({ _id: id }, function (err, project) {
-    if (err) {
-      res.status(404)
-      res.send(err)
-      return
-    }
-
-    console.log(project, req.body)
-    for (let prop in req.body) {
-      project[prop] = req.body[prop]
-    }
-
-    project.save(function (err) {
-      if (err) {
-        res.status(422)
-        res.send(err)
-        return
-      }
-      res.send()
-    })
-  })
-}
-
-export function deleteHandler (req, res) {
-  /** DELETE handler (delete project) */
-  const id = mongoose.Types.ObjectId(req.params['0'])
-  Project.remove({ _id: id }, function (err) {
-    if (err) {
-      res.send(err)
-      return
-    }
-    res.send()
-  })
-}
-
-const router = express.Router()
-router.get(/\/(\w+)?\/?$/, getHandler)
-router.post('/', postHandler)
-router.put(/\/(\w+)?\/?$/, putHandler)
-router.delete(/\/(\w+)?\/?$/, deleteHandler)
-
-export default router

+ 64 - 0
projects/__mocks__/model.js

@@ -0,0 +1,64 @@
+import projects from '../_sampleData.js'
+
+class ProjectMock {
+  constructor (data) {
+    for (let prop in data) {
+      this[prop] = data[prop]
+    }
+    this.modelName = 'project'
+    // use this switch to simulate save errors.
+    this._saveError = false
+    // use this switch to simulate remove errors.
+    this._removeError = false
+  }
+
+  static get schema () { return { obj: { __history: {} } } }
+
+  static findOne (criteria, callback) {
+    const { _id, tag } = criteria
+    let foundProject
+    if (typeof _id !== 'undefined') {
+      foundProject = projects.find(project => {
+        return (_id.equals(project._id))
+      })
+    } else if (typeof tag !== 'undefined') {
+      foundProject = projects.find(project => {
+        return (tag === project.tag && project.history.head === true)
+      })
+    }
+    if (foundProject) {
+      // return the project
+      callback(undefined, foundProject)
+    } else {
+      // else return 404
+      callback(Error('item not found'), undefined)
+    }
+  }
+
+  static find (criteria, options, callback) {
+    let { limit, skip } = options
+    const onlyHead = criteria['history.head']
+    const projectsFiltered = projects.filter(project => {
+      return project.history.head
+    })
+    // apply criteria
+    const projectsSlice = projectsFiltered.slice(skip, skip + limit)
+    if (projectsSlice) {
+      // return the array
+      callback(undefined, projectsSlice)
+    } else {
+      // if nothing can be shown, return 404
+      callback(Error('no items found'), undefined)
+    }
+  }
+
+  save () {
+    throw Error('save not implemented.')
+  }
+
+  remove () {
+    throw Error('remove not implemented.')
+  }
+}
+
+export default ProjectMock

+ 142 - 0
projects/_sampleData.js

@@ -0,0 +1,142 @@
+const projects = [{
+  _id: '18a0d6883d34293254afae42',
+  __v: 0,
+  name: 'Jungfrau',
+  tag: 'JU_CSD',
+  description: '9th generation GNDD receiver',
+  meta: [{
+    type: 'reference',
+    key: 'users',
+    value: '18b0d6883d34293254afae42',
+    description: 'Project manager'
+  }, {
+    type: 'reference',
+    key: 'files',
+    value: '38b0d6883d34293254afae42',
+    description: 'Specification'
+  }],
+  history: {
+    author_id: '18b0d6883d34293254afae42',
+    created: 1490426185914,
+    tag: 'gate1',
+    references_id: [],
+    head: false,
+    comment: 'intial version'
+  }
+}, {
+  _id: '28a0d6883d34293254afae42',
+  __v: 0,
+  name: 'Jungfrau',
+  tag: 'JU_CSD',
+  description: '9th generation GNSS receiver',
+  meta: [{
+    type: 'reference',
+    key: 'users',
+    value: '18b0d6883d34293254afae42',
+    description: 'Project manager'
+  }, {
+    type: 'reference',
+    key: 'files',
+    value: '38b0d6883d34293254afae42',
+    description: 'Specification'
+  }, {
+    type: 'reference',
+    key: 'files',
+    value: '18c0d6883d34293254afae42',
+    description: 'Chip image'
+  }],
+  history: {
+    author_id: '38b0d6883d34293254afae42',
+    created: 1490427330074,
+    tag: '',
+    references_id: ['18a0d6883d34293254afae42'],
+    head: true,
+    comment: 'Added chip image, corrected typo.'
+  }
+}, {
+  _id: '38a0d6883d34293254afae42',
+  __v: 0,
+  name: 'Dom',
+  tag: 'DO_CSD',
+  description: '8th generation GNSS receiver',
+  meta: [{
+    type: 'reference',
+    key: 'users',
+    value: '18b0d6883d34293254afae42',
+    description: 'Project manager'
+  }],
+  history: {
+    author_id: '18b0d6883d34293254afae42',
+    created: 1490421185914,
+    tag: 'gate6',
+    references_id: [],
+    head: true,
+    comment: 'intial version'
+  }
+}, {
+  _id: '48a0d6883d34293254afae42',
+  __v: 0,
+  name: 'Titlis',
+  tag: 'TI_CSD',
+  description: '7th generation GNSS receiver',
+  meta: [{
+    type: 'reference',
+    key: 'users',
+    value: '28b0d6883d34293254afae42',
+    description: 'Project manager'
+  }],
+  history: {
+    author_id: '28b0d6883d34293254afae42',
+    created: 1490227330074,
+    tag: 'gat7',
+    references_id: [],
+    head: true
+  }
+}]
+
+export const users = [{
+  _id: '18b0d6883d34293254afae42',
+  __v: 0,
+  name: 'Thomas Brauner',
+  tag: 'tbra'
+}, {
+  _id: '28b0d6883d34293254afae42',
+  __v: 0,
+  name: 'Luca Plutino',
+  tag: 'lplu'
+}, {
+  _id: '38b0d6883d34293254afae42',
+  __v: 0,
+  name: 'Benjamin Martin',
+  tag: 'bmar'
+}, {
+  _id: '48b0d6883d34293254afae42',
+  __v: 0,
+  name: 'Tomislav Cvetic',
+  tag: 'tcve'
+}]
+
+export const files = [{
+  _id: '18c0d6883d34293254afae42',
+  __v: 0,
+  type: 'image',
+  path: '/my/path/jungfrau_chip.png',
+  description: 'Microscope image of the Jungfau chip',
+  author: '48b0d6883d34293254afae42'
+}, {
+  _id: '28c0d6883d34293254afae42',
+  __v: 0,
+  type: 'document',
+  path: '/my/path/jungfrau_spec.pdf',
+  description: 'Specification for Jungfrau',
+  author: '38b0d6883d34293254afae42'
+}, {
+  _id: '38c0d6883d34293254afae42',
+  __v: 0,
+  type: 'document',
+  path: '/my/path/jungfrau_pinlist.xls',
+  description: 'Pinlist for Jungfrau',
+  author: '18b0d6883d34293254afae42'
+}]
+
+export default projects

+ 0 - 0
project/model.js → projects/model.js


+ 14 - 0
projects/route.js

@@ -0,0 +1,14 @@
+import Project from './model'
+import mongoose from 'mongoose'
+import express from 'express'
+import routeGen from '../routeGen'
+
+export const routes = routeGen(Project)
+
+const router = express.Router()
+router.get(/\/(\w+)?\/?$/, routes.get)
+router.post('/', routes.post)
+router.put(/\/(\w+)?\/?$/, routes.put)
+router.delete(/\/(\w+)?\/?$/, routes.del)
+
+export default router

+ 8 - 15
project/route.test.js → projects/route.test.js

@@ -1,4 +1,4 @@
-import router, { getHandler, postHandler, putHandler, deleteHandler } from './route'
+import router, { routes } from './route'
 import samples from './_sampleData'
 import sinon from 'sinon'
 import mongoose from 'mongoose'
@@ -19,34 +19,27 @@ describe('Route handlers', () => {
   it('handles the GET request for lists.', () => {
     // if no _id is passed, return a list of objects.
     // apply the limits correctly
-    const reqList = { params: [], query: {limit: 2.4, offset: 1.2} }
+    const reqList = { params: {}, query: {limit: 2.4, offset: 1.2} }
     const res = new ResClass()
-    const returnValue = getHandler(reqList, res)
+    const returnValue = routes.get(reqList, res)
     expect(returnValue).toBeUndefined()
-    expect(res._data).toBe(samples)
+    expect(res._data).toEqual(samples.slice(2, 4))
     expect(res._status).toBe(200)
-    expect(ProjectFind.called).toBe(true)
-    expect(ProjectFind.firstCall.args[0]).toEqual({})
-    expect(ProjectFind.firstCall.args[1]).toEqual({limit: 2, skip: 1})
   })
   it('handles the GET request for single documents.', () => {
-    const reqOne = { params: ['/58d3d6883d34293254afae42'], query: undefined }
+    const reqOne = { params: {id: '18a0d6883d34293254afae42'}, query: undefined }
     const res = new ResClass()
-    const returnValue = getHandler(reqOne, res)
+    const returnValue = routes.get(reqOne, res)
     expect(returnValue).toBeUndefined()
     expect(res._data).toBe(samples[0])
     expect(res._status).toBe(200)
-    expect(ProjectFindOne.called).toBe(true)
-    expect(ProjectFindOne.firstCall.args[0]).toEqual({_id: mongoose.Types.ObjectId('58d3d6883d34293254afae42')})
   })
   it('handles the POST request.', () => {
-    const reqOne = { params: ['/58d3d6883d34293254afae42'], query: undefined }
+    const reqOne = { params: {id: '18a0d6883d34293254afae42'}, query: undefined }
     const res = new ResClass()
-    const returnValue = postHandler(reqOne, res)
+    const returnValue = routes.post(reqOne, res)
     expect(returnValue).toBeUndefined()
     expect(res._data).toBe(samples[0])
     expect(res._status).toBe(200)
-    expect(ProjectFindOne.called).toBe(true)
-    expect(ProjectFindOne.firstCall.args[0]).toEqual({_id: mongoose.Types.ObjectId('58d3d6883d34293254afae42')})
   })
 })

+ 54 - 31
routeGen.js

@@ -4,12 +4,16 @@
  * Backend module to store React-Redux components in MongoDB.
  */
 import mongoose from 'mongoose'
-import { History } from './core/basicSchema'
+mongoose.Promise = global.Promise
 
 /**
  * Generates GET, POST, UPDATE and DELETE handlers which interact with
  * MongoDB.
  *
+ * For versioned objects, allow querying for tags or for IDs. When querying
+ * tags, return the head version, when querying for IDs, just return the
+ * element.
+ *
  * @param  {object} Model - mongoose model to use
  * @param  {boolean} versioned - use a revision control mechanism
  * @return {[type]}
@@ -23,28 +27,37 @@ function routeGen (Model, versioned = true) {
     if (typeof id === 'string' && id.length > 1) {
       try {
         // try to convert the id string to a valid ObjectId
-        id = mongoose.Types.ObjectId(id)
+        const _id = mongoose.Types.ObjectId(id)
+        // if yes, return the one item
+        Model.findOne({ _id }, function (err, item) {
+          console.log(item)
+          if (err) {
+            res.status(404)
+            res.send(err)
+            return
+          }
+          res.json(item)
+        })
       } catch (err) {
-        // If id couldn't be converted, return an error message.
-        res.status(422)
-        res.send({error: err.message})
-        return
+        // If id couldn't be converted, assume it's a tag.
+        const tag = id
+        // if yes, return the one item
+        Model.findOne({ tag, 'history.head': true }, function (err, item) {
+          if (err) {
+            res.status(404)
+            res.send(err)
+            return
+          }
+          res.json(item)
+        })
       }
-      // if yes, return the one item
-      Model.findOne({ _id: id }, function (err, item) {
-        if (err) {
-          res.status(404)
-          res.send(err)
-          return
-        }
-        res.json(item)
-      })
     } else {
       // if not, return a list of items
       // limit the number of returned items and use an offset
       const limit = req.query.limit ? Math.min(Math.max(parseInt(req.query.limit), 1), 100) : 20
       const offset = req.query.offset ? Math.max(parseInt(req.query.offset), 0) : 0
-      Model.find({}, {limit: limit, skip: offset}, function (err, items) {
+      Model.find({'history.head': true}, {limit: limit, skip: offset}, function (err, items) {
+        console.log(err, items)
         if (err) {
           res.status(404)
           res.send(err)
@@ -58,28 +71,38 @@ function routeGen (Model, versioned = true) {
   /** POST handler (insert a new item into the database) */
   const post = (req, res) => {
     // create the new item
+    console.log(req.body)
     const item = new Model(req.body)
+    console.log(item)
 
     // Check, if the model supports revision control
     if (typeof Model.schema.obj.__history !== 'undefined') {
-      item.__history = new History({
-        author: '',
-        created: Date(),
-        tag: '',
-        reference: [],
-        head: true
-      })
+      console.log('creating new history')
+      item.__history = {
+        author: 'Tomi',
+        created: new Date(),
+        tag: 'myTag',
+        reference: 'reference',
+        head: true,
+        comment: 'My Comment'
+      }
     }
+    console.log(item)
 
     // save the item
-    item.save(function (err) {
-      if (err) {
-        res.status(422)
-        res.send(err)
-        return
-      }
-      res.send({ success: `${Model.modelName} added.` })
+    console.log('before save')
+    const p = item.save()
+    console.log(p)
+    p.then(savedItem => {
+      console.log('in save', savedItem)
+      res.status(200)
+      res.send({ _id: savedItem._id })
+    }, err => {
+      console.log('in save', err)
+      res.status(422)
+      res.send(err)
     })
+    console.log('after save', p)
   }
 
   /** PUT handler (update existing project) */
@@ -116,7 +139,7 @@ function routeGen (Model, versioned = true) {
           newItem[prop] = req.body[prop]
         }
         // give it a new history with updated reference array. Set the head-indicator to true
-        newItem.__history = new History({
+        newItem.__history.create({
           author: '',
           created: Date(),
           tag: '',

+ 65 - 57
routeGen.test.js

@@ -1,38 +1,19 @@
-import routeGen from './routeGen'
-import { model, agency } from './_samples'
 import mongoose from 'mongoose'
+const table = '__routeGenTest__'
+const db = `mongodb://localhost/${table}`
+mongoose.connect(db)
+
+import routeGen from './routeGen'
+import Model from './routeGenModel'
+import { model, agency, newModel } from './_samples'
+// jest.mock('./projects/model')
+
+// import Project from './projects/model'
+const spyFindOne = jest.spyOn(Model, 'findOne')
+const spyFind = jest.spyOn(Model, 'find')
+// const spySave = jest.spyOn(Model, 'save')
+const spyRemove = jest.spyOn(Model, 'remove')
 
-class Model {
-  constructor () {
-    this.modelName = 'Gisele'
-  }
-  static find (criteria, options, callback) {
-    let { skip, limit } = options
-    const myModel = model.slice(skip, skip + limit)
-    if (myModel) {
-      callback(undefined, myModel)
-    } else {
-      callback(Error('model not found'), undefined)
-    }
-  }
-  static findOne (criteria, callback) {
-    let { _id } = criteria
-    const myModel = model.find((m) => {
-      return (mongoose.Types.ObjectId(m._id) === _id)
-    })
-    if (myModel) {
-      callback(undefined, myModel)
-    } else {
-      callback(Error('model not found'), undefined)
-    }
-  }
-  save () {
-    throw Error('save not implemented.')
-  }
-  remove () {
-    throw Error('remove not implemented.')
-  }
-}
 class Result {
   constructor () {
     this._body = null
@@ -42,9 +23,11 @@ class Result {
     this._status = value
   }
   send (data) {
+    console.log('SEND', data)
     this._body = data
   }
   json (data) {
+    console.log('JSON', data)
     this._status = 200
     this._body = data
   }
@@ -60,30 +43,55 @@ describe('routeGen for versioned models', () => {
     expect(route.put).not.toBeUndefined()
     expect(route.del).not.toBeUndefined()
   })
-  it('get without arguments calls Model.find with the right parameters', () => {
-    // call the GET handler
-    const reqList = { params: {}, query: { limit: 2, offset: 1 } }
-    route.get(reqList, res)
-    expect(res._body).toEqual(model.slice(1, 3))
-    expect(res._status).toBe(200)
-  })
-  it('return empty lists.', () => {
-    // call the GET handler
-    const reqList = { params: {}, query: { limit: 1, offset: 100 } }
-    route.get(reqList, res)
-    expect(res._body).toEqual([])
-    expect(res._status).toBe(200)
-  })
-  it('get with arguments calls Model.findOne with the right parameters', () => {
-    const reqElem = { params: { id: '58d3d6883d34293254afae42' }, query: {} }
-    route.get(reqElem, res)
-    expect(res._body).toEqual(model[0])
-    expect(res._status).toBe(200)
+  describe('get handler', () => {
+    it('get without arguments returns a list of items', () => {
+      // call the GET handler
+      const reqList = { params: {}, query: { limit: 2.1, offset: 1.1 } }
+      route.get(reqList, res)
+      expect(res._body).toEqual(model.slice(1, 3))
+      expect(res._status).toBe(200)
+      expect(spyFind).toHaveBeenCalledWith({'history.head': true}, {limit: 2, skip: 1}, expect.anything())
+    })
+    it('return empty lists.', () => {
+      // call the GET handler
+      const reqList = { params: {}, query: { limit: 1, offset: 100 } }
+      route.get(reqList, res)
+      expect(res._body).toEqual([])
+      expect(res._status).toBe(200)
+    })
+    it('get with arguments returns queried item by id', () => {
+      const reqElem = { params: { id: '18a0d6883d34293254afae42' }, query: {} }
+      route.get(reqElem, res)
+      expect(res._body).toEqual(projects[0])
+      expect(res._status).toBe(200)
+    })
+    it('get with arguments return queried item by tag (only HEAD in history)', () => {
+      const reqElem = { params: { id: 'JU_CSD' }, query: {} }
+      route.get(reqElem, res)
+      expect(res._body).toEqual(projects[1])
+      expect(res._status).toBe(200)
+    })
+    it('get returns error, when item is not found', () => {
+      const reqElem = { params: { id: '99a0d6883d34293254afae42' }, query: {} }
+      route.get(reqElem, res)
+      expect(res._body).toEqual(Error('item not found'))
+      expect(res._status).toBe(404)
+      console.log(spyFind.mock, spyFindOne.mock)
+      expect(spyFindOne.mock).toBe(true)
+    })
   })
-  it('return error when item is not found', () => {
-    const reqElem = { params: { id: '58d3d6883d34343254afae42' }, query: {} }
-    route.get(reqElem, res)
-    expect(res._body).toEqual(model[0])
-    expect(res._status).toBe(200)
+  describe('post handler', () => {
+    it('post creates a new history object', () => {
+      console.log(newModel)
+      const reqElem = { body: newModel }
+      route.post(reqElem, res)
+      console.log(res)
+      expect(res._body).toEqual({_id: expect.anything()})
+      expect(res._status).toBe(200)
+      console.log(res)
+      route.get({params: {id: res._body}, query: {}}, res)
+      // expect(res._body).toEqual(true)
+      console.log(res)
+    })
   })
 })

+ 22 - 0
routeGenModel.js

@@ -0,0 +1,22 @@
+import mongoose from 'mongoose'
+import { historySchema } from './core/basicSchema'
+
+const agencySchema = new mongoose.Schema({
+  name: String,
+  __history: [historySchema]
+})
+
+const jobSchema = new mongoose.Schema({
+  type: {type: String, enum: ['catwalk', 'photoshoot', 'commercial']},
+  description: String,
+  salery: Number
+})
+
+export const modelSchema = new mongoose.Schema({
+  name: String,
+  jobs: [jobSchema],
+  agencies: String,
+  __history: historySchema
+})
+
+export default mongoose.model('Model', modelSchema)