548 lines
18 KiB
TypeScript
548 lines
18 KiB
TypeScript
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
|
|
});
|
|
});
|
|
});
|
|
}); |