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 mongoose from 'mongoose';



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 ==='validated').length);
        should(res.body).matchEach(sample => {
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
          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');
          should(sample).have.property('added').be.type('string');
        });
        done();
      });
    });
    it('returns deleted samples for admin', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=deleted&fields[]=number&fields=status',
        auth: {basic: 'admin'},
        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 ==='deleted').length);
        should(res.body).matchEach(sample => {
          should(sample).have.property('status', 'deleted').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 ==='validated').length);
        should(res.body).matchEach(sample => {
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
          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');
          should(sample).have.property('added').be.type('string');
        });
        done();
      });
    });
    it('allows filtering by state', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new',
        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 ==='new').length);
        should(res.body).matchEach(sample => {
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'added');
          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).have.property('material_id').be.type('string');
          should(sample).have.property('note_id');
          should(sample).have.property('user_id').be.type('string');
          should(sample).have.property('added').be.type('string');
        });
        done();
      });
    });
    it('uses the given page size', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=3',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(3);
        done();
      });
    });
    it('returns results starting from first-id', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&from-id=400000000000000000000002',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000002');
        should(res.body[1]).have.property('_id', '400000000000000000000003');
        done();
      });
    });
    it('returns the right page number', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&to-page=2&page-size=2',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000006');
        done();
      });
    });
    it('works with negative page numbers', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&to-page=-1&page-size=2&from-id=400000000000000000000004',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000002');
        should(res.body[1]).have.property('_id', '400000000000000000000003');
        done();
      });
    });
    it('returns an empty array for a page number out of range', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&to-page=100&page-size=2',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(0);
        should(res.body).be.eql([]);
        done();
      });
    });
    it('returns an empty array for a page number out of negative range', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&to-page=-100&page-size=3&from-id=400000000000000000000004',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(0);
        should(res.body).be.eql([]);
        done();
      });
    });
    it('sorts the samples ascending', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=color-asc',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('color', 'black');
        should(res.body[res.body.length - 1]).have.property('color', 'natural');
        done();
      });
    });
    it('sorts the samples descending', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=number-desc',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('number', 'Rng36');
        should(res.body[1]).have.property('number', '34');
        should(res.body[res.body.length - 1]).have.property('number', '1');
        done();
      });
    });
    it('sorts the samples correctly in combination with paging', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=color-asc&page-size=2&from-id=400000000000000000000006',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000006');
        should(res.body[1]).have.property('_id', '400000000000000000000007');
        done();
      });
    });
    it('sorts the samples correctly in combination with going pages backward', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=color-desc&page-size=2&from-id=400000000000000000000004&to-page=-1',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000007');
        should(res.body[1]).have.property('_id', '400000000000000000000006');
        done();
      });
    });
    it('sorts the samples correctly for material keys', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=material.name-desc',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.property('_id', '400000000000000000000002');
        should(res.body[1]).have.property('_id', '400000000000000000000006');
        should(res.body[2]).have.property('_id', '400000000000000000000001');
        done();
      });
    });
    it('adds the status if specified', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=status',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body.find(e => e.number === '1')).have.property('status', 'validated');
        should(res.body.find(e => e.number === 'Rng36')).have.property('status', 'new');
        done();
      });
    });
    it('adds the specified measurements', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.kf.weight%20%25&fields[]=measurements.kf.standard%20deviation',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body.find(e => e.number === '1')).have.property('kf', {'weight %': null, 'standard deviation': null});
        should(res.body.find(e => e.number === 'Rng36')).have.property('kf', {'weight %': 0.6, 'standard deviation': null});
        done();
      });
    });
    it('multiplies the sample information for each spectrum', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
        auth: {basic: 'admin'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body.filter(e => e.spectrum.dpt)).have.lengthOf(2);
        should(res.body[0].spectrum).have.property('dpt', [[3997.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
        should(res.body[1].spectrum).have.property('dpt', [[3996.12558,98.00555],[3995.08519,98.03253],[3993.04480,98.02657]]);
        done();
      });
    });
    it('filters a sample property', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=type&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22type%22%2C%22values%22%3A%5B%22processed%22%5D%7D',
        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.type === 'processed').length);
        should(res.body).matchEach(sample => {
          should(sample).have.property('type', 'processed');
        });
        done();
      });
    });
    it('filters a material property', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22in%22%2C%22field%22%3A%22material.name%22%2C%22values%22%3A%5B%22Schulamid%2066%20GF%2025%20H%22%2C%22Stanyl%20TW%20200%20F8%22%5D%7D',
        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.material_id == '100000000000000000000004' || e.material_id == '100000000000000000000001').length);
        should(res.body).matchEach(sample => {
          should(sample.material.name).be.equalOneOf('Schulamid 66 GF 25 H', 'Stanyl TW 200 F8');
        });
        done();
      });
    });
    it('filters by measurement value', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=measurements.kf.weight%20%25&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
        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.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
        should(res.body).matchEach(sample => {
          should(sample.kf['weight %']).be.above(0.5);
        });
        done();
      });
    });
    it('filters by measurement value not in the fields', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&filters[]=%7B%22mode%22%3A%22gt%22%2C%22field%22%3A%22measurements.kf.weight%20%25%22%2C%22values%22%3A%5B0.5%5D%7D',
        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.measurements.filter(e => e.measurement_template == '300000000000000000000002' && e.values['weight %'] > 0.5).length);
        should(res.body[0]).have.property('number', 'Rng36');
        done();
      });
    });
    it('filters by a measurement properties property', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(2);
        should(res.body).matchEach(sample => {
          should(sample.material.properties.glass_fiber).be.eql(25);
        });
        done();
      });
    });
    it('filters and sorts by a measurement properties property', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&sort=material.properties.glass_fiber-desc&fields[]=number&fields[]=material.name&fields[]=material.properties.glass_fiber&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22material.properties.glass_fiber%22%2C%22values%22%3A%5B%2225%22%5D%7D',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(2);
        should(res.body[0].number).be.eql('Rng36');
        should(res.body[1].number).be.eql('1');
        should(res.body).matchEach(sample => {
          should(sample.material.properties.glass_fiber).be.eql(25);
        });
        done();
      });
    });
    it('filters multiple properties', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22lte%22%2C%22field%22%3A%22number%22%2C%22values%22%3A%5B%22Rng33%22%5D%7D&filters[]=%7B%22mode%22%3A%22nin%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).have.lengthOf(4);
        should(res.body[0]).be.eql({number: '1', batch: ''});
        done();
      });
    });
    it('rejects returning spectral data for a write user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.dpt',
        auth: {basic: 'janedoe'},
        httpStatus: 403
      });
    });
    it('rejects an invalid JSON string as a filters parameter', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=xx',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
      });
    });
    it('rejects an invalid filter mode', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=batch&filters[]=%7B%22mode%22%3A%22xx%22%2C%22field%22%3A%22batch%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: '"filters[0].mode" must be one of [eq, ne, lt, lte, gt, gte, in, nin, stringin]'}
      });
    });
    it('rejects an filter field not existing', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.glass_fiber&fields[]=batch&filters[]=%7B%22mode%22%3A%22eq%22%2C%22field%22%3A%22xx%22%2C%22values%22%3A%5B%221704-005%22%5D%7D',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'Invalid JSON string for filter parameter'}
      });
    });
    it('rejects unknown measurement names', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.xx',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'Measurement key not found'}
      });
    });
    it('returns a correct csv file for admins if specified', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
        contentType: /text\/csv/,
        auth: {basic: 'admin'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.text).be.eql('"_id","number","type","color","batch","condition.material","condition.weeks","condition.condition_template","material_id","note_id","user_id","added"\r\n' +
          '"400000000000000000000001","1","as-delivered/raw","black","","copper",3,"200000000000000000000001","100000000000000000000004",,"000000000000000000000002","2004-01-10T13:37:04.000Z"\r\n' +
          '"400000000000000000000002","21","as-delivered/raw","natural","1560237365","copper",3,"200000000000000000000001","100000000000000000000001","500000000000000000000001","000000000000000000000002","2004-01-10T13:37:04.000Z"');
        done();
      });
    });
    it('rejects returning a csv file for a write user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=2&output=csv',
        auth: {basic: 'janedoe'},
        httpStatus: 403
      });
    });
    it('returns the object flattened if specified', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=measurements.spectrum.device&fields[]=measurements.spectrum.dpt&page-size=1&output=flatten',
        auth: {basic: 'admin'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body[0]).have.only.keys('number', 'spectrum.device', 'spectrum.dpt.labels', 'spectrum.dpt.values');
        should(res.body[0]).have.property('number', '1');
        should(res.body[0]).have.property('spectrum.device', 'Alpha I');
        should(res.body[0]).have.property('spectrum.dpt.labels', [3997.12558, 3995.08519, 3993.0448]);
        should(res.body[0]).have.property('spectrum.dpt.values', [98.00555, 98.03253, 98.02657]);
        done();
      });
    });
    it('returns only the fields specified', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=number&fields[]=condition&fields[]=color&fields[]=material.name&fields[]=material.supplier',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        res: [{number: '1', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, color: 'black', material: {name: 'Schulamid 66 GF 25 H', supplier: 'Schulmann'}}]
      });
    });
    it('returns specified material properties fields', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&fields[]=number&fields[]=material.properties.glass_fiber&fields[]=material.name',
        auth: {basic: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        const json = require('../test/db.json');
        should(res.body).matchEach(sample => {
          const materialId = json.collections.samples.find(e => e.number === sample.number).material_id;
          const material = json.collections.materials.find(e => e._id.toString() == materialId);
          should(sample).have.only.keys('number', 'material');
          should(sample.material.name).be.eql(material.name);
          should(sample.material.properties.glass_fiber).be.eql(material.properties.glass_fiber);
        });
        done()
      });
    });
    it('rejects a from-id not in the database', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?from-id=5ea0450ed851c30a90e70894&sort=color-asc',
        auth: {basic: 'admin'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'from-id not found'}
      });
    });
    it('rejects an invalid fields parameter', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=1&fields=number',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: '"fields" must be an array'}
      });
    });
    it('rejects an unknown field name', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=new&status[]=validated&page-size=1&fields[]=xx',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'Invalid field name'}
      });
    });
    it('rejects a negative page size', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?page-size=-3',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: '"page-size" must be greater than or equal to 1'}
      });
    });
    it('rejects an invalid from-id', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?from-id=40000000000h000000000002',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: 'Invalid object id'}
      });
    });
    it('rejects a to-page without page-size', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?to-page=3',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: '"to-page" missing required peer "page-size"'}
      });
    });
    it('rejects an invalid state name', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples?status[]=xxx',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        res: {status: 'Invalid body format', details: '"status[0]" must be one of [validated, new]'}
      });
    });
    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 ==='new').length);
        should(res.body).matchEach(sample => {
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
          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');
          should(sample).have.property('added').be.type('string');
          should(sample).have.property('status').be.type('string');
          SampleModel.findById(sample._id).lean().exec((err, data) => {
            should(data).have.property('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 === 'deleted').length);
        should(res.body).matchEach(sample => {
          should(sample).have.only.keys('_id', 'number', 'type', 'color', 'batch', 'condition', 'material_id', 'note_id', 'user_id', 'status', 'added');
          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');
          should(sample).have.property('added').be.type('string');
          should(sample).have.property('status').be.type('string');
          SampleModel.findById(sample._id).lean().exec((err, data) => {
            should(data).have.property('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 /samples/count', () => {
    it('returns the correct number of samples', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples/count',
        auth: {basic: 'admin'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        const json = require('../test/db.json');
        should(res.body.count).be.eql(json.collections.samples.length);
        done();
      });
    });
    it('works with an API key', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples/count',
        auth: {key: 'janedoe'},
        httpStatus: 200
      }).end((err, res) => {
        if (err) return done(err);
        const json = require('../test/db.json');
        should(res.body.count).be.eql(json.collections.samples.length);
        done();
      });
    });
    it('rejects unauthorized requests', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/samples/count',
        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: 'processed', 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', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', 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: 'processed', 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', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
      });
    });
    it ('filters out spectral data for a write user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
      });
    });
    it ('returns spectral data for an admin user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'admin'},
        httpStatus: 200,
        res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
      });
    });
    it('returns a deleted sample for a dev/admin user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/400000000000000000000005',
        auth: {basic: 'admin'},
        httpStatus: 200,
        res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', 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: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
      });
    });
    it('keeps unchanged properties', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        req: {type: 'as-delivered/raw', 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: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
        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', 'as-delivered/raw');
          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','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: 'as-delivered/raw'}
      }).end((err, res) => {
        if (err) return done(err);
        should(res.body).be.eql({_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
          if (err) return done (err);
          should(data).have.property('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: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
        SampleModel.findById('400000000000000000000001').lean().exec((err, data: any) => {
          if (err) return done (err);
          should(data).have.property('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: 'as-delivered/raw', color: 'natural', batch: '1560237365', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material_id: '100000000000000000000001', note_id: '500000000000000000000001', user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'});
        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', 'as-delivered/raw');
          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','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: 'processed', 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', 'processed');
          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','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('creates a changelog', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        req: {type: 'processed', color: 'signalviolet', batch: '114531', condition: {condition_template: '200000000000000000000003'}, material_id: '100000000000000000000002', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
        log: {
          collection: 'samples',
          dataAdd: {
            status: 'new'
          },
          dataIgn: ['notes', 'note_id']
        }
      });
    });
    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: 'processed'}
      }).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 an unknown material id', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {type: 'processed', 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: 'processed', 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: 'processed', 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: 'processed', 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: 'Invalid object id'}
      });
    });
    it('rejects an invalid id', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/10000000000h000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 404,
        req: {type: 'processed', 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 greater 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('rejects a not accepted type', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {type: 'xx'},
        res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
      });
    });
    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: 'as-delivered/raw', color: 'black', batch: '', condition: {}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'new', added: '2004-01-10T13:37:04.000Z'}
      });
    });
    it('rejects an old version of a condition template', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
        res: {status: 'Old template version not allowed'}
      });
    });
    it('allows keeping an old version of a condition template', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000004',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {condition: {p1: 36, condition_template: '200000000000000000000004'}},
        res: {_id: '400000000000000000000004', number: '32', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000005', note_id: '500000000000000000000003', user_id: '000000000000000000000003', status: 'new', added: '2004-01-10T13:37:04.000Z'}
      });
    });
    it('rejects 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: 'processed', 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 dev/admin user', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {},
        res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {condition_template: '200000000000000000000001', material: 'copper', weeks: 3}, material_id: '100000000000000000000004', note_id: null, user_id: '000000000000000000000002', status: 'validated', added: '2004-01-10T13:37:04.000Z'}
      });
    });
    it('rejects requests from a read user', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'user'},
        httpStatus: 403,
        req: {type: 'processed', 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: 'processed', 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: 'processed', 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', 'as-delivered/raw');
          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','deleted');
          should(data).have.property('note_id', null);
          done();
        });
      });
    });
    it('creates a changelog', done => {
      TestHelper.request(server, done, {
        method: 'delete',
        url: '/sample/400000000000000000000001',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        log: {
          collection: 'samples',
          skip: 1,
          dataAdd: {status: 'deleted'}
        }
      });
    });
    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/dev 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','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', 'deleted');
          });
          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('GET /sample/number/{number}', () => {
    it('returns the right sample', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/33',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        res: {_id: '400000000000000000000003', number: '33', type: 'processed', 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', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
      });
    });
    it('works with an API key', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/33',
        auth: {key: 'janedoe'},
        httpStatus: 200,
        res: {_id: '400000000000000000000003', number: '33', type: 'processed', 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', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {comment: '', sample_references: [{sample_id: '400000000000000000000004', relation: 'as-delivered/raw to sample'}], custom_fields: {'not allowed for new applications': true}}, measurements: [{_id: '800000000000000000000003', sample_id: '400000000000000000000003', values: {val1: 1}, measurement_template: '300000000000000000000003'}], status: 'new', user: 'admin'}
      });
    });
    it('returns a deleted sample for a dev/admin user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/Rng33',
        auth: {basic: 'admin'},
        httpStatus: 200,
        res: {_id: '400000000000000000000005', number: 'Rng33', type: 'as-delivered/raw', color: 'black', batch: '1653000308', condition: {condition_template: '200000000000000000000003'}, material: {_id: '100000000000000000000005', name: 'Amodel A 1133 HS', supplier: 'Solvay', group: 'PPA', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 33, carbon_fiber: 0}, numbers: ['5514262406']}, notes: {}, measurements: [], status: 'deleted', user: 'admin'}
      });
    });
    it ('filters out spectral data for a write user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/1',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
      });
    });
    it ('returns spectral data for an admin user', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/1',
        auth: {basic: 'admin'},
        httpStatus: 200,
        res: {_id: '400000000000000000000001', number: '1', type: 'as-delivered/raw', color: 'black', batch: '', condition: {material: 'copper', weeks: 3, condition_template: '200000000000000000000001'}, material: {numbers: ['5513933405'], _id: '100000000000000000000004', name: 'Schulamid 66 GF 25 H', properties: {material_template: '130000000000000000000003', mineral: 0, glass_fiber: 25, carbon_fiber: 0}, group: 'PA66', supplier: 'Schulmann'}, user: 'janedoe', notes: {}, measurements: [{_id: '800000000000000000000001', sample_id: '400000000000000000000001', values: {dpt: [[ 3997.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]],device: 'Alpha I'}, measurement_template: '300000000000000000000001'}, {_id: '800000000000000000000007', sample_id: '400000000000000000000001', values: {dpt: [[ 3996.12558, 98.00555 ], [ 3995.08519, 98.03253 ], [ 3993.0448, 98.02657 ]], device: 'Alpha II'}, measurement_template: '300000000000000000000001'}], status: 'validated'}
      });
    });
    it('returns 403 for a write user when requesting a deleted sample', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/Rng33',
        auth: {basic: 'janedoe'},
        httpStatus: 403
      });
    });
    it('returns 404 for an unknown sample', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/Rng883',
        auth: {basic: 'janedoe'},
        httpStatus: 404
      });
    });
    it('rejects an invalid id', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/xx-xx',
        auth: {basic: 'janedoe'},
        httpStatus: 404
      });
    });
    it('rejects unauthorized requests', done => {
      TestHelper.request(server, done, {
        method: 'get',
        url: '/sample/number/33',
        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','new');
          done();
        });
      });
    });
    it('restores associated measurements', 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'});
        MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000005')}).lean().exec((err, data: any) => {
          if (err) return done(err);
          should(data).matchEach(measurement => {
            should(measurement).have.property('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('PUT /sample/validate/{id}', () => {
    it('sets the status', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000003',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {}
      }).end((err, res) => {
        if (err) return done (err);
        should(res.body).be.eql({status: 'OK'});
        SampleModel.findById('400000000000000000000003').lean().exec((err, data: any) => {
          if (err) return done(err);
          should(data).have.property('status','validated');
          done();
        });
      });
    });
    it('validates associated measurements', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000003',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {}
      }).end((err, res) => {
        if (err) return done (err);
        should(res.body).be.eql({status: 'OK'});
        MeasurementModel.find({sample_id: mongoose.Types.ObjectId('400000000000000000000003')}).lean().exec((err, data: any) => {
          if (err) return done(err);
          should(data).matchEach(measurement => {
            should(measurement).have.property('status', 'validated')
          });
          done();
        });
      });
    });
    it('allows validating a sample without condition', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000006',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {}
      }).end((err, res) => {
        if (err) return done (err);
        should(res.body).be.eql({status: 'OK'});
        SampleModel.findById('400000000000000000000006').lean().exec((err, data: any) => {
          if (err) return done(err);
          should(data).have.property('status','validated');
          done();
        });
      });
    });
    it('allows validating a sample without measurements', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000004',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {}
      }).end((err, res) => {
        if (err) return done (err);
        should(res.body).be.eql({status: 'OK'});
        SampleModel.findById('400000000000000000000004').lean().exec((err, data: any) => {
          if (err) return done(err);
          should(data).have.property('status','validated');
          done();
        });
      });
    });
    it('rejects an API key', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000003',
        auth: {key: 'admin'},
        httpStatus: 401,
        req: {}
      });
    });
    it('rejects a write user', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000003',
        auth: {basic: 'janedoe'},
        httpStatus: 403,
        req: {}
      });
    });
    it('returns 404 for an unknown sample', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/000000000000000000000003',
        auth: {basic: 'admin'},
        httpStatus: 404,
        req: {}
      });
    });
    it('rejects unauthorized requests', done => {
      TestHelper.request(server, done, {
        method: 'put',
        url: '/sample/validate/400000000000000000000003',
        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: 'as-delivered/raw', 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', 'status', 'added');
        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', 'as-delivered/raw');
        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');
        should(res.body).have.property('status', 'new');
        should(res.body).have.property('added').be.type('string');
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
        done();
      });
    });
    it('stores the sample', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        req: {color: 'black', type: 'as-delivered/raw', 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', 'as-delivered/raw');
          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','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('creates a changelog', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        req: {color: 'black', type: 'as-delivered/raw', 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'}]}},
        log: {
          collection: 'samples',
          dataAdd: {
            number: 'Rng37',
            user_id: '000000000000000000000002',
            status: 'new'
          },
          dataIgn: ['notes', 'note_id']
        }
      });
    });
    it('stores the custom fields', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 200,
        req: {color: 'black', type: 'as-delivered/raw', 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: 'as-delivered/raw', 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', 'status', 'added');
        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', 'as-delivered/raw');
        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');
        should(res.body).have.property('status', 'new');
        should(res.body).have.property('added').be.type('string');
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1500);
        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: 'as-delivered/raw', 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', 'status', 'added');
        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', 'as-delivered/raw');
        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');
        should(res.body).have.property('status', 'new');
        should(res.body).have.property('added').be.type('string');
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
        done();
      });
    });
    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: 'as-delivered/raw', 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 for a write user', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', 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('allows a sample number for an admin user', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'admin'},
        httpStatus: 200,
        req: {number: 'Rng34', color: 'black', type: 'as-delivered/raw', 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', 'status', 'added');
        should(res.body).have.property('_id').be.type('string');
        should(res.body).have.property('number', 'Rng34');
        should(res.body).have.property('color', 'black');
        should(res.body).have.property('type', 'as-delivered/raw');
        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', '000000000000000000000003');
        should(res.body).have.property('status', 'new');
        should(res.body).have.property('added').be.type('string');
        should(new Date().getTime() - new Date(res.body.added).getTime()).be.below(1000);
        done();
      });
    });
    it('rejects an existing sample number for an admin user', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'admin'},
        httpStatus: 400,
        req: {number: 'Rng33', color: 'black', type: 'as-delivered/raw', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
        res: {status: 'Sample number already taken'}
      });
    });
    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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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 an old version of a condition template', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {color: 'black', type: 'as-delivered/raw', batch: '1560237365', condition: {p1: 36, condition_template: '200000000000000000000004'}, material_id: '100000000000000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
        res: {status: 'Old template version not allowed'}
      });
    });
    it('rejects a missing color', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {type: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', batch: '1560237365', material_id: '10000000000h000000000001', notes: {comment: 'Testcomment', sample_references: [{sample_id: '400000000000000000000003', relation: 'part to this sample'}]}},
        res: {status: 'Invalid body format', details: 'Invalid object id'}
      });
    });
    it('rejects a not accepted type', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {basic: 'janedoe'},
        httpStatus: 400,
        req: {color: 'black', type: 'xx', batch: '1560237365', material_id: '100000000000000000000001', notes: {comment: 'Testcomment'}},
        res: {status: 'Invalid body format', details: '"type" must be one of [as-delivered/raw, processed]'}
      });
    });
    it('rejects an API key', done => {
      TestHelper.request(server, done, {
        method: 'post',
        url: '/sample/new',
        auth: {key: 'janedoe'},
        httpStatus: 401,
        req: {color: 'black', type: 'as-delivered/raw', 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: 'as-delivered/raw', 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: 'as-delivered/raw', 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
      });
    });
  });
});
