import should from 'should/as-function'; import ModelFileModel from '../models/model_file'; import TestHelper from "../test/helper"; import ModelModel from '../models/model'; import UserModel from '../models/user'; import mongoose from 'mongoose'; describe('/model', () => { 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 /model/groups', () => { it('returns all groups for an admin user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/groups', 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.models.length); should(res.body).matchEach(group => { should(group).have.only.keys('group', 'models'); should(group).have.property('group').be.type('string'); should(group.models).matchEach(model => { should(model).have.only.keys('_id', 'name', 'url'); should(model).have.property('_id').be.type('string'); should(model).have.property('name').be.type('string'); should(model).have.property('url').be.type('string'); }); }); done(); }); }); it('returns all allowed groups for a predict user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/groups', auth: {basic: 'customer'}, httpStatus: 200, }).end((err, res) => { if (err) return done (err); should(res.body).have.lengthOf(1); should(res.body).matchEach(group => { should(group).have.only.keys('group', 'models'); should(group).have.property('group').be.type('string'); should(group).have.property('models', [{_id: '120000000000000000000001', name: 'Model A', url: 'http://model-a.com'}]); }); done(); }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/model/groups', auth: {key: 'janedoe'}, httpStatus: 401, }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'get', url: '/model/groups', httpStatus: 401, }); }); }); describe('POST /model/{group}', () => { it('adds a new model', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'Model C', url: 'http://model-c.com'} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => { if (err) return done(err); const model = res.models.find(e => e.name === 'Model C'); should(model).have.property('url', 'http://model-c.com'); done(); }); }); }); it('adds a new group', done => { TestHelper.request(server, done, { method: 'post', url: '/model/classification', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'Model 0.1', url: 'http://model-0-1.com'} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelModel.findOne({group: 'classification'}).lean().exec((err, res) => { if (err) return done(err); should(res).have.only.keys('_id', 'group', 'models', '__v'); should(res).have.property('group', 'classification'); should(res.models[0]).have.only.keys('_id', 'name', 'url'); should(res.models[0]).have.property('name', 'Model 0.1'); should(res.models[0]).have.property('url', 'http://model-0-1.com'); done(); }); }); }); it('replaces a model', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 200, req: {name: 'Model A', url: 'http://model-a-new.com'} }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => { if (err) return done(err); const model = res.models.find(e => e.name === 'Model A'); should(model).have.property('url', 'http://model-a-new.com'); done(); }); }); }); it('rejects an empty name', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 400, req: {name: '', url: 'http://model-c.com'}, res:{status: 'Invalid body format', details: '"name" is not allowed to be empty'} }); }); it('rejects a missing name', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 400, req: {url: 'http://model-c.com'}, res:{status: 'Invalid body format', details: '"name" is required'} }); }); it('rejects an invalid URL', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 400, req: {name: 'Model C', url: 'model-c'}, res:{status: 'Invalid body format', details: '"url" must be a valid uri'} }); }); it('rejects a missing URL', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'admin'}, httpStatus: 400, req: {name: 'Model C'}, res:{status: 'Invalid body format', details: '"url" is required'} }); }); it('rejects a write user', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {basic: 'janedoe'}, httpStatus: 403, req: {name: 'Model C', url: 'http://model-c.com'} }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', auth: {key: 'admin'}, httpStatus: 401, req: {name: 'Model C', url: 'http://model-c.com'} }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'post', url: '/model/VN', httpStatus: 401, req: {name: 'Model C', url: 'http://model-c.com'} }); }); }); describe('DELETE /model/{group}/{name}', () => { it('deletes the model', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/Model%20A', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelModel.findOne({group: 'VN'}).lean().exec((err, res) => { if (err) return done(err); should(res).have.only.keys('_id', 'group', 'models'); should(res).have.property('group', 'VN'); should(res.models[0]).have.only.keys('_id', 'name', 'url'); should(res.models[0]).have.property('name', 'Model B'); should(res.models[0]).have.property('url', 'http://model-b.com'); done(); }); }); }); it('deletes the group, if empty afterwards', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/Moisture/Model%201', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelModel.find({group: 'Moisture'}).lean().exec((err, res) => { if (err) return done(err); should(res).have.lengthOf(0); done(); }); }); }); it ('removes the model_id from all user.models', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/Model%20A', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); UserModel.find({models: mongoose.Types.ObjectId("120000000000000000000001")}).lean().exec((err, res) => { if (err) return done(err); should(res).have.lengthOf(0); done(); }); }); }); it('returns 404 for an unknown group', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/xxx/Model%201', auth: {basic: 'admin'}, httpStatus: 404 }); }); it('returns 404 for an unknown model', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/xxx', auth: {basic: 'admin'}, httpStatus: 404 }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/Model%20A', auth: {key: 'admin'}, httpStatus: 401 }); }); it('rejects a write user', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/Model%20A', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/VN/Model%20A', httpStatus: 401 }); }); }); describe('GET /model/files', () => { it('rejects a write user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/files', auth: {basic: 'janedoe'}, httpStatus: 403, }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/model/files', auth: {key: 'admin'}, httpStatus: 401, }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'get', url: '/model/files', httpStatus: 401, }); }); }); describe('GET /model/file/{name}', (() => { it('returns the binary data', done => { TestHelper.request(server, done, { method: 'get', url: '/model/file/modela', auth: {basic: 'admin'}, httpStatus: 200, contentType: 'application/octet-stream; charset=utf-8', }).end((err, res) => { if (err) return done (err); should(res.body.toString()).be.eql('binary data'); done(); }); }); it('returns the binary data for an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/model/file/modela', auth: {key: 'admin'}, httpStatus: 200, contentType: 'application/octet-stream; charset=utf-8', }).end((err, res) => { if (err) return done (err); should(res.body.toString()).be.eql('binary data'); done(); }); }); it('returns 404 for an unknown name', done => { TestHelper.request(server, done, { method: 'get', url: '/model/file/modelx', auth: {basic: 'admin'}, httpStatus: 404 }) }); it('rejects requests from a write user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/file/modela', auth: {basic: 'janedoe'}, httpStatus: 403 }) }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'get', url: '/model/file/modela', httpStatus: 401 }) }); })); describe('POST /model/file/{name}', () => { it('stores the data', done => { TestHelper.request(server, done, { method: 'post', url: '/model/file/modelb', auth: {basic: 'admin'}, httpStatus: 200, reqContentType: 'application/octet-stream', req: 'another binary data' }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => { if (err) return done (err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'name', 'data', '__v'); should(data[0]).have.property('name', 'modelb'); should(data[0].data.buffer.toString()).be.eql('another binary data'); done(); }); }); }); it('stores the data with an API key', done => { TestHelper.request(server, done, { method: 'post', url: '/model/file/modelb', auth: {key: 'admin'}, httpStatus: 200, reqContentType: 'application/octet-stream', req: 'another binary data' }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); ModelFileModel.find({name: 'modelb'}).lean().exec((err, data) => { if (err) return done (err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'name', 'data', '__v'); should(data[0]).have.property('name', 'modelb'); should(data[0].data.buffer.toString()).be.eql('another binary data'); done(); }); }); }); it('overwrites existing data', done => { TestHelper.request(server, done, { method: 'post', url: '/model/file/modela', auth: {basic: 'admin'}, httpStatus: 200, reqContentType: 'application/octet-stream', req: 'another binary data' }).end((err, res) => { if (err) return done (err); should(res.body).be.eql({status: 'OK'}); ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => { if (err) return done (err); should(data).have.lengthOf(1); should(data[0]).have.only.keys('_id', 'name', 'data', '__v'); should(data[0]).have.property('name', 'modela'); should(data[0].data.buffer.toString()).be.eql('another binary data'); done(); }); }); }); it('rejects requests from a write user', done => { TestHelper.request(server, done, { method: 'post', url: '/model/file/modelb', auth: {basic: 'janedoe'}, httpStatus: 403, req: 'another binary data' }); }); it('rejects unauthorized requests', done => { TestHelper.request(server, done, { method: 'post', url: '/model/file/modelb', httpStatus: 401, req: 'another binary data' }); }); }); describe('DELETE /model/file/{name}', () => { it('deletes the data', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/file/modela', auth: {basic: 'admin'}, httpStatus: 200 }).end((err, res) => { if (err) return done(err); should(res.body).be.eql({status: 'OK'}); ModelFileModel.find({name: 'modela'}).lean().exec((err, data) => { if (err) return done(err); should(data).have.lengthOf(0); done(); }); }); }); it('returns 404 for an unknown name', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/file/modelx', auth: {basic: 'admin'}, httpStatus: 404 }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/file/modela', auth: {key: 'admin'}, httpStatus: 401 }); }); it('rejects a write user', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/file/modela', auth: {basic: 'janedoe'}, httpStatus: 403 }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'delete', url: '/model/file/modela', httpStatus: 401 }); }); }); describe('GET /model/authorized/{url}', () => { it('returns OK for every model for admins', done => { TestHelper.request(server, done, { method: 'get', url: '/model/authorized/xx', auth: {basic: 'admin'}, httpStatus: 200, res: {status: 'OK'} }); }); it('returns OK for a specified model for a predict user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/authorized/http%3A%2F%2Fmodel-a.com', auth: {basic: 'customer'}, httpStatus: 200, res: {status: 'OK'} }); }); it('rejects a model not specified for a predict user', done => { TestHelper.request(server, done, { method: 'get', url: '/model/authorized/http%3A%2F%2Fmodel-b.com', auth: {basic: 'customer'}, httpStatus: 403 }); }); it('rejects an API key', done => { TestHelper.request(server, done, { method: 'get', url: '/model/authorized/xx', auth: {key: 'admin'}, httpStatus: 401 }); }); it('rejects an unauthorized request', done => { TestHelper.request(server, done, { method: 'get', url: '/model/authorized/http%3A%2F%2Fmodel-b.com', httpStatus: 401 }); }); }); });