Skip to content

Commit

Permalink
Add tests for profile editor API
Browse files Browse the repository at this point in the history
  • Loading branch information
danielfdsilva committed Sep 11, 2018
1 parent 1b1d630 commit 7c3f173
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 18 deletions.
2 changes: 1 addition & 1 deletion app/routes/projects--source-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ export default [
.where('project_id', projId)
.where('type', 'profile');

return reply({statusCode: 200, message: 'Profile settigns uploaded'});
return reply({statusCode: 200, message: 'Profile settings uploaded'});
} catch (err) {
console.log('err', err);
return reply(Boom.badImplementation(err));
Expand Down
9 changes: 9 additions & 0 deletions app/s3/structure.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,12 @@ export function putObjectFromFile (bucket, name, filepath) {
});
});
}

export function putObject (bucket, file, stream) {
return new Promise((resolve, reject) => {
s3.putObject(bucket, file, stream, (err, etag) => {
if (err) return reject(err);
return resolve(etag);
});
});
}
12 changes: 4 additions & 8 deletions app/s3/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'fs-extra';
import Promise from 'bluebird';

import s3, { bucket } from './';
import { removeObject, putObjectFromFile, listObjects, emptyBucket } from './structure';
import { removeObject, putObjectFromFile, listObjects, emptyBucket, putObject } from './structure';

const readFile = Promise.promisify(fs.readFile);

Expand Down Expand Up @@ -115,14 +115,10 @@ export function getJSONFileContents (file) {
.then(result => JSON.parse(result));
}

// Put file from stream
// Put object
// Proxy of putObject function, assuming the bucket.
export function putFileStream (file, stream) {
return new Promise((resolve, reject) => {
s3.putObject(bucket, file, stream, (err, etag) => {
if (err) return reject(err);
return resolve(etag);
});
});
return putObject(bucket, file, stream);
}

// Put file
Expand Down
6 changes: 4 additions & 2 deletions app/utils/osrm-profile.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';
import renderProfile from './default.profile.template';

function toLua (element) {
export function toLua (element) {
let properties = [];

// Array
Expand All @@ -12,7 +12,9 @@ function toLua (element) {
// Object
} else if (typeof element === 'object') {
Object.keys(element).forEach(key => {
properties.push(` ["${key}"] = ${toLua(element[key])}`);
// Ensure correct indentation.
const lua = toLua(element[key]).toString().replace(/\n/gm, '\n ');
properties.push(` ["${key}"] = ${lua}`);
});
return `{\n${properties.join(',\n')}\n}`;

Expand Down
243 changes: 243 additions & 0 deletions test/test-projects-source-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import db from '../app/db';
import { setupStructure as setupDdStructure } from '../app/db/structure';
import { setupStructure as setupStorageStructure } from '../app/s3/structure';
import { fixMeUp } from './utils/data';
import { getOSRMProfileDefaultSpeedSettings, toLua } from '../app/utils/osrm-profile';

var options = {
connection: {port: 2000, host: '0.0.0.0'}
Expand Down Expand Up @@ -455,4 +456,246 @@ describe('Projects source data', function () {
});
});
});

describe('GET /projects/{projId}/source-data/editor -- profile', function () {
it('should fail without a type', function () {
return instance.injectThen({
method: 'GET',
url: '/projects/1000/source-data/editor'
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"type" is required/);
});
});

it('should fail without a type different than profile', function () {
return instance.injectThen({
method: 'GET',
url: '/projects/1000/source-data/editor?type=invalid'
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"type" must be one of \[profile\]/);
});
});

it('should fail if project is not found', function () {
return instance.injectThen({
method: 'GET',
url: '/projects/0000/source-data/editor?type=profile'
}).then(res => {
assert.equal(res.statusCode, 404, 'Status code is 404');
var result = res.result;
assert.equal(result.message, 'Project not found');
});
});

it('should fail if project setup is not completed', function () {
return instance.injectThen({
method: 'GET',
url: '/projects/1000/source-data/editor?type=profile'
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.equal(result.message, 'Project setup not completed');
});
});

it('should return the profile settings', function () {
return instance.injectThen({
method: 'GET',
url: '/projects/2000/source-data/editor?type=profile'
}).then(res => {
assert.equal(res.statusCode, 200, 'Status code is 200');
var result = res.result;
assert.deepEqual(result, getOSRMProfileDefaultSpeedSettings());
});
});
});

describe('POST /projects/{projId}/source-data/editor -- profile', function () {
it('should fail without a type', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/1000/source-data/editor'
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"type" is required/);
});
});

it('should fail without a type different than profile', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/1000/source-data/editor?type=invalid'
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"type" must be one of \[profile\]/);
});
});

it('should fail with missing keys', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/2000/source-data/editor?type=profile',
payload: {

}
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"speed_profile" is required/);
});
});

const emptySettings = {
speed_profile: {},
surface_speeds: {},
tracktype_speeds: {},
smoothness_speeds: {},
maxspeed_table_default: {},
maxspeed_table: {}
};

it('should fail with invalid section keys', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/2000/source-data/editor?type=profile',
payload: Object.assign({}, emptySettings, {
invalid: {}
})
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.equal(result.message, '"invalid" is not allowed');
});
});

it('should fail with invalid speed keys', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/2000/source-data/editor?type=profile',
payload: Object.assign({}, emptySettings, {
speed_profile: {
'invalid key': 10
}
})
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"invalid key" is not allowed/);
});
});

it('should fail with invalid speed values', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/2000/source-data/editor?type=profile',
payload: Object.assign({}, emptySettings, {
speed_profile: {
'valid_key': 'invalid'
}
})
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.match(result.message, /"valid_key" must be a number/);
});
});

it('should fail if project is not found', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/0000/source-data/editor?type=profile',
payload: emptySettings
}).then(res => {
assert.equal(res.statusCode, 404, 'Status code is 404');
var result = res.result;
assert.equal(result.message, 'Project not found');
});
});

it('should fail if project setup is not completed', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/1000/source-data/editor?type=profile',
payload: emptySettings
}).then(res => {
assert.equal(res.statusCode, 400, 'Status code is 400');
var result = res.result;
assert.equal(result.message, 'Project setup not completed');
});
});

it('should render the object to lua', function () {
const data = {
foo: 'bar',
another: 'baz'
};
assert.equal(toLua(data), `{
["foo"] = "bar",
["another"] = "baz"
}`);
});

it('should render the nested object to lua', function () {
const data = {
foo: 'bar',
another: {
baz: 10,
more: 20,
double: {
nesting: 'all the way'
}
},
bax: 10
};
assert.equal(toLua(data), `{
["foo"] = "bar",
["another"] = {
["baz"] = 10,
["more"] = 20,
["double"] = {
["nesting"] = "all the way"
}
},
["bax"] = 10
}`);
});

it('should update the profile settings', function () {
return instance.injectThen({
method: 'POST',
url: '/projects/2000/source-data/editor?type=profile',
payload: Object.assign({}, emptySettings, {
speed_profile: {
highway: '100',
secondary: 20
}
})
}).then(res => {
assert.equal(res.statusCode, 200, 'Status code is 200');
var result = res.result;
assert.equal(result.message, 'Profile settings uploaded');
})
.then(() => {
return db('projects_source_data')
.select('*')
.where('project_id', 2000)
.where('name', 'profile')
.first();
})
.then(({data}) => {
assert.deepEqual(data.settings, Object.assign({}, emptySettings, {
speed_profile: {
highway: 100,
secondary: 20
}
}));
});
});
});
});
2 changes: 1 addition & 1 deletion test/test-projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ describe('Projects', function () {
let project = res.result;
assert.deepEqual(project.sourceData, {
profile: {
type: 'file',
type: 'default',
files: [
{
'id': 2000,
Expand Down
16 changes: 10 additions & 6 deletions test/utils/data.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import fs from 'fs';
import config from '../../app/config';
import db from '../../app/db';
import { bucket } from '../../app/s3/';
import { putObjectFromFile } from '../../app/s3/structure';
import { putObjectFromFile, putObject } from '../../app/s3/structure';
import { getOSRMProfileDefaultSpeedSettings, renderProfileFile } from '../../app/utils/osrm-profile';

function readJSONSync (file) {
return JSON.parse(fs.readFileSync(file, 'utf8'));
Expand Down Expand Up @@ -846,11 +847,12 @@ export function project1200 () {

// Project 2000 in active state with one scenarios and all files.
// Files represent real data from Sergipe, Brazil
// Profile is default.
export function project2000 () {
return project({
'id': 2000,
'name': 'Sergipe, Brazil',
'description': 'Townhalls in a part of Sergipe, brazil.',
'description': 'Townhalls in a part of Sergipe, Brazil. Includes a default profile to allow editing.',
'status': 'active',
'bbox': JSON.stringify(ADMIN_AREAS_BBOX),
'created_at': '2017-02-01T12:00:06.000Z',
Expand Down Expand Up @@ -888,16 +890,18 @@ export function project2000 () {
]))
.then(() => projectAA(getAdminAreasForProject(2000)))
.then(() => projectOrigins(getOriginsForProject(2000)))
.then(() => putObjectFromFile(bucket, 'project-2000/profile_000000', FILE_PROFILE))
.then(() => putObject(bucket, 'project-2000/profile_000000', renderProfileFile(getOSRMProfileDefaultSpeedSettings())))
.then(() => putObjectFromFile(bucket, 'project-2000/origins_000000', FILE_ORIGINS))
.then(() => putObjectFromFile(bucket, 'project-2000/admin-bounds_000000', FILE_ADMIN))
.then(() => projectSourceData([
{
'id': 2000,
'name': 'profile',
'type': 'file',
'project_id': 2000
// 'data':
'type': 'default',
'project_id': 2000,
'data': {
'settings': getOSRMProfileDefaultSpeedSettings()
}
},
{
'id': 2001,
Expand Down

0 comments on commit 7c3f173

Please sign in to comment.