import should from 'should/as-function'; import SampleModel from '../models/sample'; import NoteModel from '../models/note'; import NoteFieldModel from '../models/note_field'; import MeasurementModel from '../models/measurement'; import TestHelper from "../test/helper"; import globals from '../globals'; import mongoose from 'mongoose'; // TODO: generate output for ML in format DPT -> data, implement filtering, field selection // TODO: generate csv // TODO: filter by not completely filled/no measurements // TODO: write script for data import // TODO: allow adding sample numbers for existing samples // TODO: Do not allow validation or measurement entry without condition describe('/sample', () => { let server; before(done => TestHelper.before(done)); beforeEach(done => server = TestHelper.beforeEach(server, done)); afterEach(done => TestHelper.afterEach(server, done)); after(done => TestHelper.after(done)); describe('GET /samples', () => { it('returns all samples', done => { TestHelper.request(server, done, { method: 'get', url: '/samples', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); should(sample).have.property('color').be.type('string'); should(sample).have.property('batch').be.type('string'); should(sample).have.property('condition').be.type('object'); should(sample.condition).have.property('condition_template').be.type('string'); should(sample).have.property('material_id').be.type('string'); should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); }); done(); }); }); it('works with an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/samples', auth: {key: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.validated).length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); should(sample).have.property('color').be.type('string'); should(sample).have.property('batch').be.type('string'); should(sample).have.property('condition').be.type('object'); should(sample.condition).have.property('condition_template').be.type('string'); should(sample).have.property('material_id').be.type('string'); should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); }); done(); }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/samples', httpStatus: 401 }); }); }); describe('GET /samples/{state}', () => { it('returns all new samples', done => { TestHelper.request(server, done, { method: 'get', url: '/samples/new', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); let asyncCounter = res.body.length; should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status ===globals.status.new).length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); should(sample).have.property('color').be.type('string'); should(sample).have.property('batch').be.type('string'); should(sample).have.property('condition').be.type('object'); if (Object.keys(sample.condition).length > 0) { should(sample.condition).have.property('condition_template').be.type('string'); } should(sample).have.property('material_id').be.type('string'); should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); SampleModel.findById(sample._id).lean().exec((err, data) => { should(data).have.property('status',globals.status.new); if (--asyncCounter === 0) { done(); } }); }); }); }); it('returns all deleted samples', done => { TestHelper.request(server, done, { method: 'get', url: '/samples/deleted', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); let asyncCounter = res.body.length; should(res.body).have.lengthOf(json.collections.samples.filter(e => e.status === -1).length); should(res.body).matchEach(sample => { should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(sample).have.property('_id').be.type('string'); should(sample).have.property('number').be.type('string'); should(sample).have.property('type').be.type('string'); should(sample).have.property('color').be.type('string'); should(sample).have.property('batch').be.type('string'); should(sample).have.property('condition').be.type('object'); should(sample.condition).have.property('condition_template').be.type('string'); should(sample.condition).have.property('condition_template').be.type('string'); should(sample.condition).have.property('condition_template').be.type('string'); should(sample).have.property('material_id').be.type('string'); should(sample).have.property('note_id'); should(sample).have.property('user_id').be.type('string'); SampleModel.findById(sample._id).lean().exec((err, data) => { should(data).have.property('status',globals.status.deleted); if (--asyncCounter === 0) { done(); } }); }); }); }); it('rejects requests from a write user', done => { TestHelper.request(server, done, { method: 'get', url: '/samples/new', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/samples/new', auth: {key: 'admin'}, httpStatus: 401 }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/samples/new', httpStatus: 401 }); }); }); describe('GET /sample/{id}', () => { it('returns the right sample', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000000000000000003', auth: {basic: 'janedoe'}, httpStatus: 200, res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'} }); }); it('works with an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000000000000000003', auth: {key: 'janedoe'}, httpStatus: 200, res: {_id: '400000000000000000000003', number: '33', type: 'part', color: 'black', batch: '1704-005', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'granulate to sample'}], custom_fields: {'not allowed for new applications': true}}, user: 'admin'} }); }); it('returns a deleted sample for a maintain/admin user', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000000000000000005', auth: {basic: 'admin'}, httpStatus: 200, res: {_id: '400000000000000000000005', number: 'Rng33', type: 'granulate', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', mineral: 0, glass_fiber: 33, carbon_fiber: 0, numbers: [{color: 'black', number: '5514262406'}]}, notes: {}, user: 'admin'} }); }); it('returns 403 for a write user when requesting a deleted sample', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000000000000000005', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('returns 404 for an unknown sample', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/000000000000000000000005', auth: {basic: 'janedoe'}, httpStatus: 404 }); }); it('rejects an invalid id', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000h00000000000005', auth: {basic: 'janedoe'}, httpStatus: 404 }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/400000000000000000000005', httpStatus: 401 }); }); }); describe('PUT /sample/{id}', () => { it('returns the right sample', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, req: {}, res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'} }); }); it('keeps unchanged properties', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, req: {type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', notes: {}} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data).have.property('_id'); should(data).have.property('number', '1'); should(data).have.property('color', 'black'); should(data).have.property('type', 'granulate'); should(data).have.property('batch', ''); should(data).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}); should(data.material_id.toString()).be.eql('100000000000000000000004'); should(data.user_id.toString()).be.eql('000000000000000000000002'); should(data).have.property('status',globals.status.validated); should(data).have.property('note_id', null); done(); }); }); }); it('keeps only one unchanged parameter', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, req: {type: 'granulate'} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.property('status',globals.status.validated); done(); }); }); }); it('keeps an unchanged condition', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.property('status',globals.status.validated); done(); }); }); }); it('keeps unchanged notes', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000002', auth: {basic: 'janedoe'}, httpStatus: 200, req: {notes: {comment: 'Stoff gesperrt', sample_references: []}} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({_id: '400000000000000000000002', number: '21', type: 'granulate', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002'}); SampleModel.findById('400000000000000000000002').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data).have.property('_id'); should(data).have.property('number', '21'); should(data).have.property('color', 'natural'); should(data).have.property('type', 'granulate'); should(data).have.property('batch', '1560237365'); should(data.condition).have.property('material', 'copper'); should(data.condition).have.property('weeks', 3); should(data.condition.condition_template.toString()).be.eql('200000000000000000000001'); should(data.material_id.toString()).be.eql('100000000000000000000001'); should(data.user_id.toString()).be.eql('000000000000000000000002'); should(data).have.property('status',globals.status.validated); should(data.note_id.toString()).be.eql('500000000000000000000001'); done(); }); }); }); it('changes the given properties', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200, req: {type: 'part', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end(err => { if (err) return done (err); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done (err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data).have.property('_id'); should(data).have.property('number', '1'); should(data).have.property('color', 'signalviolet'); should(data).have.property('type', 'part'); should(data).have.property('batch', '114531'); should(data).have.property('condition', {condition_template: '200000000000000000000003'}); should(data.material_id.toString()).be.eql('100000000000000000000002'); should(data.user_id.toString()).be.eql('000000000000000000000002'); should(data).have.property('status',globals.status.new); should(data).have.property('note_id'); NoteModel.findById(data.note_id).lean().exec((err, data: any) => { if (err) return done (err); should(data).have.property('_id'); should(data).have.property('comment', 'Testcomment'); should(data).have.property('sample_references'); should(data.sample_references).have.lengthOf(1); should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003'); should(data.sample_references[0]).have.property('relation', 'part to this sample'); done(); }); }); }); }); it('adjusts the note_fields correctly', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000003', auth: {basic: 'admin'}, httpStatus: 200, req: {notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'value 1'}}} }).end(err => { if (err) return done(err); NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.property('qty', 1); NoteFieldModel.findOne({name: 'field1'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.property('qty', 1); done(); }); }); }); }); it('deletes old note_fields', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000004', auth: {basic: 'admin'}, httpStatus: 200, req: {notes: {comment: 'Testcomment', sample_references: []}} }).end(err => { if (err) return done (err); NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => { if (err) return done (err); should(data).be.null(); done(); }); }); }); it('keeps untouched notes', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000002', auth: {basic: 'janedoe'}, httpStatus: 200, req: {type: 'part'} }).end((err, res) => { if (err) return done (err); NoteModel.findById(res.body.note_id).lean().exec((err, data) => { if (err) return done (err); should(data).not.be.null(); should(data).have.property('comment', 'Stoff gesperrt'); should(data).have.property('sample_references').have.lengthOf(0); done(); }); }); }); it('deletes old notes', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000004', auth: {basic: 'admin'}, httpStatus: 200, req: {notes: {comment: 'Testcomment', sample_references: []}} }).end(err => { if (err) return done (err); NoteModel.findById('500000000000000000000003').lean().exec((err, data) => { if (err) return done (err); should(data).be.null(); done(); }); }); }); it('rejects a color not defined for the material', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Color not available for material'} }); }); it('rejects an unknown material id', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '000000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Material not available'} }); }); it('rejects a sample number', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {number: 25, type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"number" is not allowed'} }); }); it('rejects an invalid sample reference', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Sample reference not available'} }); }); it('rejects an invalid material id', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'} }); }); it('rejects an invalid id', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/10000000000h000000000001', auth: {basic: 'janedoe'}, httpStatus: 404, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('rejects not specified condition parameters', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'copper', weeks: 3, xxx: 44, condition_template: '200000000000000000000001'}}, res: {status: 'Invalid body format', details: '"xxx" is not allowed'} }); }); it('rejects a condition parameter not in the value range', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'xx', weeks: 3, condition_template: '200000000000000000000001'}}, res: {status: 'Invalid body format', details: '"material" must be one of [copper, hot air]'} }); }); it('rejects a condition parameter below minimum range', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'copper', weeks: 0, condition_template: '200000000000000000000001'}}, res: {status: 'Invalid body format', details: '"weeks" must be larger than or equal to 1'} }); }); it('rejects a condition parameter above maximum range', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'copper', weeks: 10.5, condition_template: '200000000000000000000001'}}, res: {status: 'Invalid body format', details: '"weeks" must be less than or equal to 10'} }); }); it('rejects an invalid condition template', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'copper', weeks: 3, condition_template: '200000000000h00000000001'}}, res: {status: 'Condition template not available'} }); }); it('rejects an unknown condition template', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}}, res: {status: 'Condition template not available'} }); }); it('allows keeping an empty condition empty', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000006', auth: {basic: 'janedoe'}, httpStatus: 200, req: {condition: {}}, res: {_id: '400000000000000000000006', number: 'Rng36', type: 'granulate', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'} }); }); it('rejects an changing back to an empty condition', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 400, req: {condition: {}}, res: {status: 'Condition template not available'} }); }); it('rejects editing a deleted sample', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000005', auth: {basic: 'admin'}, httpStatus: 403, req: {} }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {key: 'janedoe'}, httpStatus: 401, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('rejects changes for samples from another user for a write user', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000003', auth: {basic: 'janedoe'}, httpStatus: 403, req: {} }); }); it('accepts changes for samples from another user for a maintain/admin user', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200, req: {}, res: {_id: '400000000000000000000001', number: '1', type: 'granulate', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002'} }); }); it('rejects requests from a read user', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', auth: {basic: 'user'}, httpStatus: 403, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); it('returns 404 for an unknown sample', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/000000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 404, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }) it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/400000000000000000000001', httpStatus: 401, req: {type: 'part', color: 'signalviolet', batch: '114531', material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, }); }); }); describe('DELETE /sample/{id}', () => { it('sets the status to deleted', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => { if (err) return done(err); should(data).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data).have.property('_id'); should(data).have.property('number', '1'); should(data).have.property('color', 'black'); should(data).have.property('type', 'granulate'); should(data).have.property('batch', ''); should(data.condition).have.property('material', 'copper'); should(data.condition).have.property('weeks', 3); should(data.condition.condition_template.toString()).be.eql('200000000000000000000001'); should(data.material_id.toString()).be.eql('100000000000000000000004'); should(data.user_id.toString()).be.eql('000000000000000000000002'); should(data).have.property('status',globals.status.deleted); should(data).have.property('note_id', null); done(); }); }); }); it('keeps the notes of the sample', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000002', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); NoteModel.findById('500000000000000000000001').lean().exec((err, data) => { if (err) return done(err); should(data).have.only.keys('_id', 'comment', 'sample_references', '__v'); should(data).have.property('comment', 'Stoff gesperrt'); should(data).have.property('sample_references').with.lengthOf(0); done(); }); }); }); it('adjusts the note_fields correctly', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000004', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); NoteFieldModel.findOne({name: 'not allowed for new applications'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.property('qty', 1); NoteFieldModel.findOne({name: 'another_field'}).lean().exec((err, data) => { if (err) return done(err); should(data).be.null(); done(); }); }); }); }); it('keeps references to this sample', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000003', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); setTimeout(() => { // background action takes some time before we can check NoteModel.findById('500000000000000000000003').lean().exec((err, data: any) => { if (err) return done(err); should(data).have.property('sample_references').with.lengthOf(1); should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003'); should(data.sample_references[0]).have.property('relation', 'part to sample'); done(); }); }, 100); }); }); it('lets admin/maintain users delete samples of other users', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000001', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000001').lean().exec((err, data) => { if (err) return done(err); should(data).have.property('status',globals.status.deleted); done(); }); }); }); it('deletes associated measurements', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000001', auth: {basic: 'janedoe'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000001')}).lean().exec((err, data: any) => { if (err) return done(err); should(data).matchEach(sample => { should(sample).have.property('status', -1); }); done(); }); }); }); it('rejects deleting samples of other users for write users', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000004', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects an invalid id', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000h00000000004', auth: {basic: 'janedoe'}, httpStatus: 404 }); }); it('rejects requests from a read user', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000004', auth: {basic: 'user'}, httpStatus: 403 }); }); it('returns 404 for an unknown id', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/000000000000000000000004', auth: {basic: 'janedoe'}, httpStatus: 404 }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000001', auth: {key: 'janedoe'}, httpStatus: 401 }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'delete', url: '/sample/400000000000000000000001', httpStatus: 401 }); }); }); describe('PUT /sample/restore/{id}', () => { it('sets the status', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/restore/400000000000000000000005', auth: {basic: 'admin'}, httpStatus: 200, req: {} }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); SampleModel.findById('400000000000000000000005').lean().exec((err, data: any) => { if (err) return done(err); should(data).have.property('status',globals.status.new); done(); }); }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/restore/400000000000000000000005', auth: {key: 'admin'}, httpStatus: 401, req: {} }); }); it('rejects a write user', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/restore/400000000000000000000005', auth: {basic: 'janedoe'}, httpStatus: 403, req: {} }); }); it('returns 404 for an unknown sample', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/restore/000000000000000000000005', auth: {basic: 'admin'}, httpStatus: 404, req: {} }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'put', url: '/sample/restore/400000000000000000000005', httpStatus: 401, req: {} }); }); }); describe('POST /sample/new', () => { it('returns the right sample', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Rng37'); should(res.body).have.property('color', 'black'); should(res.body).have.property('type', 'granulate'); should(res.body).have.property('batch', '1560237365'); should(res.body).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}); should(res.body).have.property('material_id', '100000000000000000000001'); should(res.body).have.property('note_id').be.type('string'); should(res.body).have.property('user_id', '000000000000000000000002'); done(); }); }); it('stores the sample', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end(err => { if (err) return done (err); SampleModel.find({number: 'Rng37'}).lean().exec((err, data: any) => { if (err) return done (err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', '__v'); should(data[0]).have.property('_id'); should(data[0]).have.property('number', 'Rng37'); should(data[0]).have.property('color', 'black'); should(data[0]).have.property('type', 'granulate'); should(data[0]).have.property('batch', '1560237365'); should(data[0]).have.property('condition', {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}); should(data[0].material_id.toString()).be.eql('100000000000000000000001'); should(data[0].user_id.toString()).be.eql('000000000000000000000002'); should(data[0]).have.property('status',globals.status.new); should(data[0]).have.property('note_id'); NoteModel.findById(data[0].note_id).lean().exec((err, data: any) => { if (err) return done (err); should(data).have.property('_id'); should(data).have.property('comment', 'Testcomment'); should(data).have.property('sample_references'); should(data.sample_references).have.lengthOf(1); should(data.sample_references[0].sample_id.toString()).be.eql('400000000000000000000003'); should(data.sample_references[0]).have.property('relation', 'part to this sample'); done(); }); }) }); }); it('stores the custom fields', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [], custom_fields: {field1: 'a', field2: 'b', 'not allowed for new applications': true}}} }).end((err, res) => { if (err) return done (err); NoteModel.findById(res.body.note_id).lean().exec((err, data: any) => { if (err) return done(err); should(data).have.property('_id'); should(data).have.property('comment', 'Testcomment'); should(data).have.property('sample_references').have.lengthOf(0); should(data).have.property('custom_fields'); should(data.custom_fields).have.property('field1', 'a'); should(data.custom_fields).have.property('field2', 'b'); should(data.custom_fields).have.property('not allowed for new applications', true); NoteFieldModel.find({name: 'field1'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('qty', 1); NoteFieldModel.find({name: 'field2'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('qty', 1); NoteFieldModel.find({name: 'not allowed for new applications'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(1); should(data[0]).have.property('qty', 3); done(); }); }); }); }); }); }); it('stores a new sample location as 1', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'johnnydoe'}, httpStatus: 200, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Fe1'); should(res.body).have.property('color', 'black'); should(res.body).have.property('type', 'granulate'); should(res.body).have.property('batch', '1560237365'); should(res.body).have.property('material_id', '100000000000000000000001'); should(res.body).have.property('note_id').be.type('string'); should(res.body).have.property('user_id', '000000000000000000000004'); done(); }); }); it('accepts a sample without condition', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 200, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }).end((err, res) => { if (err) return done (err); should(res.body).have.only.keys('_id', 'number', 'color', 'type', 'batch', 'condition', 'material_id', 'note_id', 'user_id'); should(res.body).have.property('_id').be.type('string'); should(res.body).have.property('number', 'Rng37'); should(res.body).have.property('color', 'black'); should(res.body).have.property('type', 'granulate'); should(res.body).have.property('batch', '1560237365'); should(res.body).have.property('condition', {}); should(res.body).have.property('material_id', '100000000000000000000001'); should(res.body).have.property('note_id').be.type('string'); should(res.body).have.property('user_id', '000000000000000000000002'); done(); }); }); it('rejects a color not defined for the material', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'green', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Color not available for material'} }); }); it('rejects an unknown material id', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '000000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Material not available'} }); }); it('rejects a sample number', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {number: 'Rng34', color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"number" is not allowed'} }); }); it('rejects an invalid sample reference', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '000000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Sample reference not available'} }); }); it('rejects an invalid condition_template id', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '20000h000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects a not existing condition_template id', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '000000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects not specified condition parameters', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3, xxx: 23, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects missing condition parameters', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects condition parameters not in the value range', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'xxx', weeks: 3, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects a condition parameter below minimum range', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 0, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects a condition parameter above maximum range', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 11, condition_template: '20000000000000000000001'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects a condition without condition template', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', condition: {material: 'copper', weeks: 3}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Condition template not available'} }); }); it('rejects a missing color', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"color" is required'} }); }); it('rejects a missing type', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"type" is required'} }); }); it('rejects a missing batch', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"batch" is required'} }); }); it('rejects a missing material id', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" is required'} }); }); it('rejects an invalid material id', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'janedoe'}, httpStatus: 400, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}}, res: {status: 'Invalid body format', details: '"material_id" with value "10000000000h000000000001" fails to match the required pattern: /[0-9a-f]{24}/'} }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {key: 'janedoe'}, httpStatus: 401, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); it('rejects requests from a read user', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', auth: {basic: 'user'}, httpStatus: 403, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'post', url: '/sample/new', httpStatus: 401, req: {color: 'black', type: 'granulate', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}} }); }); }); describe('GET /sample/notes/fields', () => { it('returns all fields', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/notes/fields', auth: {basic: 'user'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.note_fields.length); should(res.body).matchEach(material => { should(material).have.only.keys('name', 'qty'); should(material).have.property('qty').be.type('number'); }); done(); }); }); it('works with an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/notes/fields', auth: {key: 'user'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); const json = require('../test/db.json'); should(res.body).have.lengthOf(json.collections.note_fields.length); should(res.body).matchEach(material => { should(material).have.only.keys('name', 'qty'); should(material).have.property('qty').be.type('number'); }); done(); }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/sample/notes/fields', httpStatus: 401 }); }); }); });