IEEE.org     |     IEEE Xplore Digital Library     |     IEEE Standards     |     IEEE Spectrum     |     More Sites

Unverified Commit 1a92d256 authored by Anthony Palumbo's avatar Anthony Palumbo Committed by GitHub
Browse files

Merging to create a demo "release" (#87)

parent eb3ee7b4
......@@ -5,4 +5,4 @@ This is the repository for the Seneca Park Zoo Society Conservation project. Thi
If you have any questions or would like to make a PR, please reach out to one of the team members.
##### Last updated Oct 19, 2019
##### Last updated Nov 11, 2019
......@@ -11,6 +11,7 @@ const app = express();
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
next();
});
app.use(bodyParser.urlencoded({
......@@ -20,4 +21,4 @@ app.use(bodyParser.json());
app.use(express.static('./client'));
app.use('/api/v1', routes.v1);
module.exports = app;
\ No newline at end of file
module.exports = app;
This diff is collapsed.
......@@ -13,14 +13,19 @@
"author": "",
"license": "ISC",
"dependencies": {
"pg": "^7.11.0",
"express": "^4.17.0",
"body-parser": "^1.19.0",
"dotenv": "^8.0.0"
"csvtojson": "^2.0.10",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"lodash": ">=4.17.13",
"moment": "^2.24.0",
"multer": "^1.4.2",
"pg": "^7.12.1"
},
"devDependencies": {
"eslint": "^5.16.0",
"jest": "^24.8.0",
"eslint-utils": ">=1.4.1",
"jest": "^24.9.0",
"supertest": "^4.0.2"
}
}
......@@ -2,6 +2,10 @@ const request = require('supertest');
const app = require('../../app');
const { setup, teardown, loadSQL } = require('../setup');
const EXPECTED_ASSET_TYPE1 = { id: 1, name: 'Tree', description: null };
const EXPECTED_ASSET_TYPE2 = { id: 2, name: 'Lemur', description: 'Mammals of the order Primates, divided into 8 families and consisting of 15 genera and around 100 existing species. They are native only to the island of Madagascar.' };
const EXPECTED_ASSET_TYPE3 = { id: 3, name: 'Bison', description: 'Bison are large, even-toed ungulates in the genus Bison within the subfamily Bovinae.' };
describe('GET/POST assetDefinitions', () => {
beforeAll(async () => {
jest.setTimeout(30000);
......@@ -61,3 +65,113 @@ describe('GET/POST assetDefinitions', () => {
});
});
});
describe('GET assetTypes', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
// create some default asset types
await loadSQL('../schema/sample-data-assetTypes.sql');
});
// Clean up after the tests are finished.
afterAll(async () => {
await teardown();
});
it('gets all asset types', async () => {
await request(app)
.get('/api/v1/assetTypes')
.expect(200)
.then((res) => {
expect(res.body.rows).toEqual(
expect.arrayContaining([
expect.objectContaining(EXPECTED_ASSET_TYPE1),
expect.objectContaining(EXPECTED_ASSET_TYPE2),
expect.objectContaining(EXPECTED_ASSET_TYPE3)
])
);
expect(res.body.rows).toHaveLength(3);
});
});
});
describe('POST assetPropTypes', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
// create some default asset types
await loadSQL('../schema/sample-data-assetTypes.sql');
});
afterAll(async () => {
await teardown();
});
it('gets all asset property types', async () => {
await request(app)
.post('/api/v1/assetPropTypes')
.send({'assetTypeID' : '1'})
.expect(200)
.then((res) => {
expect(res.body.rows).toEqual(
expect.arrayContaining([
expect.objectContaining(EXPECTED_ASSET_TYPE1),
expect.objectContaining(EXPECTED_ASSET_TYPE2),
expect.objectContaining(EXPECTED_ASSET_TYPE3)
])
);
expect(res.body.rows).toHaveLength(1);
});
});
});
describe('POST assetPropsByTypeID', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
// create some default asset types
await loadSQL('../schema/sample-data-assetTypes.sql');
});
afterAll(async () => {
await teardown();
});
it('gets all asset property types', async () => {
await request(app)
.post('/api/v1/assetPropsByTypeID')
.send({'assetTypeID' : '1'})
.expect(200)
.then((res) => {
expect(res.body.rows).toEqual(
expect.arrayContaining([
expect.objectContaining(EXPECTED_ASSET_TYPE1),
expect.objectContaining(EXPECTED_ASSET_TYPE2),
expect.objectContaining(EXPECTED_ASSET_TYPE3)
])
);
expect(res.body.rows).toHaveLength(3);
});
});
});
// TODO - tests for PUT CSV endpoint
describe('PUT CSV', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
// create some default asset definitions
await loadSQL('../schema/sample-data-assetTypes.sql');
});
// Clean up after the tests are finished.
afterAll(async () => {
await teardown();
});
});
......@@ -104,7 +104,7 @@ describe('GET/POST assets', () => {
id: 1
},
location: {
lattitude: 1.5,
latitude: 1.5,
longitude: -1.5
},
properties: [
......@@ -141,7 +141,7 @@ describe('GET/POST assets', () => {
expect(typeof data).toBe('object');
//"Randomly" checking that the data loaded from Sample-data-1.sql is present
expect(contains(data, 'sponsor_name', 'Bronx Zoo'));
expect(contains(data, 'sponsor_name', 'Seneca Park Zoo'));
expect(contains(data, 'sponsor_name', 'Seneca Park Zoo Society'));
expect(contains(data, 'asset_id', '21'));
expect(contains(data, 'asset_type', 'Bison'));
done();
......
......@@ -2,6 +2,7 @@ const request = require('supertest');
const app = require('../../app');
const { setup, teardown, loadSQL } = require('../setup');
const { createTestAsset } = require('../utils');
const ENDPOINT = '/api/v1/bbox-assets';
......@@ -15,18 +16,6 @@ const NULL_BOX = {
longitude_max: null
};
/**
* @param {number} latitude latitude of the asset
* @param {number} longitude longitude of the asset
* @param {number} [projectId] project ID of the asset
*/
const createAsset = async (latitude, longitude, projectId = 1) =>
global.dbPool.query(
`INSERT INTO asset (project_id, asset_type_id, location)
VALUES ($1, 1, ST_POINT($2, $3))`,
[projectId, latitude, longitude]
);
describe('GET bbox-assets', () => {
beforeAll(async () => {
await setup();
......@@ -51,7 +40,7 @@ describe('GET bbox-assets', () => {
});
it('returns location of asset with min = max when there is one asset', async () => {
await createAsset(34, 27);
await createTestAsset(34, 27);
const EXPECTED_BBOX = {
latitude_min: 34,
......@@ -72,9 +61,9 @@ describe('GET bbox-assets', () => {
it('returns bounding box of multiple assets', async () => {
await Promise.all([
createAsset(1, 3),
createAsset(-1, -1),
createAsset(-2, 0)
createTestAsset(1, 3),
createTestAsset(-1, -1),
createTestAsset(-2, 0)
]);
const EXPECTED_BBOX = {
......@@ -101,7 +90,7 @@ describe('GET bbox-assets', () => {
});
it('can filter assets by project ID', async () => {
await Promise.all([createAsset(4, 5, 1), createAsset(7, 8, 2)]);
await Promise.all([createTestAsset(4, 5, 1), createTestAsset(7, 8, 2)]);
const EXPECTED_BBOX = {
latitude_min: 7,
......
const request = require('supertest');
const querystring = require('querystring');
const app = require('../../app');
const { setup, teardown, loadSQL } = require('../setup');
const { createTestAsset } = require('../utils');
const GEOMETRY_ENDPOINT = '/api/v1/assets/geometrySearch';
const ENVELOPE_ENDPOINT = `${GEOMETRY_ENDPOINT}/envelope`;
const DISTANCE_ENDPOINT = `${GEOMETRY_ENDPOINT}/distance`;
const POLYGON_ENDPOINT = `${GEOMETRY_ENDPOINT}/polygon`;
describe('GET assets/geometrySearch/envelope', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
await loadSQL('../schema/sample-data-emptyProjects.sql');
});
afterAll(async () => {
await teardown();
});
afterEach(async () => {
await global.dbPool.query('DELETE FROM asset');
});
it('returns 200 response', async () => {
const envelopeQuery = querystring.encode({
minimumLatitude: '-1.1',
minimumLongitude: '-1.1',
maximumLatitude: '1.1',
maximumLongitude: '1.1'
});
await request(app)
.get(ENVELOPE_ENDPOINT + `?${envelopeQuery}`)
.expect(200);
});
it('returns only assets within envelope', async () => {
await createTestAsset(.5, .25);
await createTestAsset(5, 5);
const envelopeQuery = querystring.encode({
minimumLatitude: '-1.1',
minimumLongitude: '-1.1',
maximumLatitude: '1.1',
maximumLongitude: '1.1'
});
await request(app)
.get(ENVELOPE_ENDPOINT + `?${envelopeQuery}`)
.expect(200)
.then((res) => {
expect(res.body).toHaveLength(1);
let retAsset = res.body[0];
expect(retAsset.lat).toBe(.5);
expect(retAsset.lon).toBe(.25);
});
});
});
describe('GET assets/geometrySearch/distance', () => {
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
await loadSQL('../schema/sample-data-emptyProjects.sql');
});
afterAll(async () => {
await teardown();
});
afterEach(async () => {
await global.dbPool.query('DELETE FROM asset');
});
it('returns 200 response', async () => {
const distanceQuery = querystring.encode({
latitude: '-1.1',
longitude: '-1.1',
radiusMeters: '1000',
});
await request(app)
.get(DISTANCE_ENDPOINT + `?${distanceQuery}`)
.expect(200);
});
});
describe('GET assets/geometrySearch/polygon', () => {
// Helper function
let pack = (lat, lon) => { return { latitude: lat, longitude: lon}; };
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
await loadSQL('../schema/sample-data-emptyProjects.sql');
});
afterAll(async () => {
teardown();
});
afterEach(async () => {
await global.dbPool.query('DELETE FROM asset');
});
it('returns 200 response', async () => {
const validBody = { coordinates : [pack('13.3', '33'), pack('12', '44'), pack('12', '3')]};
await request(app)
.get(POLYGON_ENDPOINT)
.send(validBody)
.expect(200);
});
});
......@@ -377,4 +377,4 @@ describe('PUT Projects', () => {
expect(res.body['errors'][0]).toHaveProperty('reason', 'Expected a number between 1 and 2147483647');
});
});
});
\ No newline at end of file
});
const request = require('supertest');
const app = require('../../app');
const { setup, teardown, loadSQL } = require('../setup');
const ENDPOINT = '/api/v1/assets/properties/temporalSearch';
describe('GET assets/properties/temporalSearch', () => {
const polygon_search = {
'geometry': {
'type': 'Polygon',
'coordinates' : [[-16, 44], [-18, 44], [-18, 48], [-16, 48], [-16, 44]]
},
'sponsor': 'seneca park zoo society',
'asset_type': 'fire',
'project': 'Madagascar reforesting project'
};
const circle_search = {
'geometry': {
'type': 'Circle',
'coordinates' : [-16, 44],
'radius': '10000'
},
'start_date': '2020-05-20',
'end_date': '2020-05-25'
};
beforeAll(async () => {
jest.setTimeout(30000);
await setup();
await loadSQL('../schema/sample-data-emptyProjects.sql');
});
afterAll(async () => {
await teardown();
});
afterEach(async () => {
await global.dbPool.query('DELETE FROM asset');
});
it('returns HTTP 200 with "Polygon" search', async () => {
await request(app)
.get(ENDPOINT)
.send(polygon_search)
.expect(200);
});
it('returns HTTP 200 with "Circle" search', async () => {
await request(app)
.get(ENDPOINT)
.send(circle_search)
.expect(200);
});
});
......@@ -7,7 +7,7 @@ const createTestAsset = async (latitude, longitude, projectId = 1) =>
global.dbPool.query(
`INSERT INTO asset (project_id, asset_type_id, location)
VALUES ($1, 1, ST_POINT($2, $3))`,
[projectId, latitude, longitude]
[projectId, longitude, latitude]
);
module.exports = { createTestAsset };
\ No newline at end of file
module.exports = { createTestAsset };
const { getAssetTypes, getAssetPropsByTypeID, getAssetPropTypes } = require('../assetDefinitions.controller');
const assetDefinitionsDb = require('../../db/assetDefinitions.db');
describe('assetDefinitions.controller.getAssetTypes', () => {
let req;
let res;
let next;
let expectedAssetTypes;
let data;
beforeEach(() => {
req = {};
res = {
json: jest.fn()
};
next = jest.fn();
expectedAssetTypes = [{}];
data = { rows: expectedAssetTypes };
assetDefinitionsDb.findAssetTypes = jest.fn(async () => data);
});
it('returns all asset types in DB', async () => {
await getAssetTypes(req, res, next);
expect(res.json).toHaveBeenCalledWith(data);
});
});
describe('assetDefinitions.controller.getAssetPropsByTypeID', () => {
let req;
let res;
let next;
let expectedAssetTypes;
let data;
beforeEach(() => {
req = { valid : { assetTypeID: 1 } };
res = {
json: jest.fn()
};
next = jest.fn();
expectedAssetTypes = [{}];
data = { rows: expectedAssetTypes };
assetDefinitionsDb.findAssetPropsByTypeID = jest.fn(async () => data);
});
it('returns all asset types in DB', async () => {
await getAssetPropsByTypeID(req, res, next);
expect(res.json).toHaveBeenCalledWith(data);
});
});
describe('assetDefinitions.controller.getAssetPropsTypes', () => {
let req;
let res;
let next;
let expectedAssetTypes;
let data;
beforeEach(() => {
req = { valid : { assetTypeID: 1 } };
res = {
json: jest.fn()
};
next = jest.fn();
expectedAssetTypes = [{}];
data = { rows: expectedAssetTypes };
assetDefinitionsDb.findAssetPropTypes = jest.fn(async () => data);
});
it('returns all asset types in DB', async () => {
await getAssetPropTypes(req, res, next);
expect(res.json).toHaveBeenCalledWith(data);
});
});
describe('assetDefinitions.controller.storeCSV', () => {
let req;
let res;
let next;
let assetTypeId;
let expected;
beforeEach(() => {
req = {
body: {},
file: {}
};
// Clear the response
res = {
json: jest.fn()
};
next = jest.fn();
expected = { success: true };
assetTypeId = 1;
assetDefinitionsDb.storeCSV = jest.fn(async () => expected);
});
// TODO - tests for storeCSV function in controller
});
const { distanceFind, envelopeFind, polygonFind } = require('../geometrySearch.controller');
const geomDb = require('../../db/geometrySearch.db');
describe('geometrySearch.controller.envelopeFind', () => {
let req;
let res;
let next;
let expected;
beforeEach(() => {
// Clear the request
req = {
valid: {},
query: {}
};
// Clear the response
res = {
json: jest.fn(),
send: jest.fn(),
status: jest.fn(() => res)
};
next = jest.fn();
expected = [{}];
geomDb.envelopeFind = jest.fn(async () => expected);
});
it('queries the database with the correct parameters', async () =>{
req.valid['minimumLatitude'] = 10;
req.valid['minimumLongitude'] = 11;
req.valid['maximumLatitude'] = 20;
req.valid['maximumLongitude'] = 21