From eebbdda064c045230785dfe163e5821e4957ff15 Mon Sep 17 00:00:00 2001 From: vjrj Date: Thu, 14 Jun 2018 17:55:38 +0200 Subject: [PATCH] Rest api updated --- imports/api/Rest/Rest.js | 322 +++++++++++++++++++++++++++ imports/api/Subscriptions/methods.js | 66 +++--- imports/api/Users/Users.js | 1 + imports/startup/server/rest.js | 162 +------------- package-lock.json | 25 +-- package.json | 1 + test/rest.test.js | 219 ++++++++++++++++++ 7 files changed, 591 insertions(+), 205 deletions(-) create mode 100644 imports/api/Rest/Rest.js create mode 100644 test/rest.test.js diff --git a/imports/api/Rest/Rest.js b/imports/api/Rest/Rest.js new file mode 100644 index 0000000..b3c63b9 --- /dev/null +++ b/imports/api/Rest/Rest.js @@ -0,0 +1,322 @@ +/* global Restivus */ +/* eslint-disable import/no-absolute-path */ + +import { Meteor } from 'meteor/meteor'; +import { NumberBetween } from '/imports/modules/server/other-checks'; +import Fires from '/imports/api/Fires/Fires'; +import { check } from 'meteor/check'; +import SiteSettings from '/imports/api/SiteSettings/SiteSettings'; +import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires'; +import { countRealFires } from '/imports/api/ActiveFires/server/countFires'; +import { whichAreFalsePositives, firesUnion } from '/imports/api/FalsePositives/server/publications'; +import FalsePositives from '/imports/api/FalsePositives/FalsePositives'; +import Industries from '/imports/api/Industries/Industries'; +import Subscriptions from '/imports/api/Subscriptions/Subscriptions'; +import jsend from 'jsend'; +import { Random } from 'meteor/random'; +import { subscriptionsInsert } from '/imports/api/Subscriptions/methods.js'; + +const debug = false; + +const uptime = new Date(); + + +function fail(e) { + return jsend.error(`Unexpected error in REST call: ${e}`); +} + +function defaultFailParams(e) { + return jsend.error(`Wrong REST params: ${e}`); +} + +function checkAuthToken(token) { + if (token !== Meteor.settings.private.internalApiToken) { + const message = `Unauthorized auth token '${token}' in REST API`; + console.warn(message); + return jsend.error(message); + } + return undefined; +} + +function checkLatLonDist(km, lat, lng) { + check(lng, NumberBetween(-180, 180)); + check(lat, NumberBetween(-90, 90)); + check(km, NumberBetween(0, Meteor.isDevelopment ? 1000 : 100)); +} + + +// export +const apiV1 = new Restivus({ + useDefaultAuth: true, + apiPath: 'api', + version: 'v1', + prettyJson: true +}); + +// Generates: POST on /api/users and GET, DELETE /api/users/:id for +// Meteor.users collection +/* apiV1.addCollection(Meteor.users, { + * excludedEndpoints: ['getAll', 'put', 'delete', 'patch', 'get'], + * routeOptions: { + * authRequired: true + * }, + * endpoints: { + * post: { + * authRequired: false + * }, + * delete: { + * roleRequired: 'admin' + * } + * } + * }); */ + +apiV1.addCollection(Fires, { + excludedEndpoints: ['put', 'post', 'patch', 'delete'], + // excludedEndpoints: ['getAll', 'put', 'post', 'patch', 'delete'], + routeOptions: { + authRequired: false + }, + endpoints: { + get: { + action: function getFire() { + return Fires.findOne(new Meteor.Collection.ObjectID(this.urlParams.id)); + } + } + } +}); + +// Maps to: /api/v1/status/last-fire-check +apiV1.addRoute('status/last-fire-check', { authRequired: false }, { + get: function get() { + return SiteSettings.findOne({ name: 'last-fire-check' }); + } +}); + +// Maps to: /api/v1/status/last-fire-detected +apiV1.addRoute('status/last-fire-detected', { authRequired: false }, { + get: function get() { + return ActiveFiresCollection.findOne({}, { sort: { when: -1 } }); + } +}); + +// Maps to: /api/v1/status/active-fires-count +apiV1.addRoute('status/active-fires-count', { authRequired: false }, { + get: function get() { + return { total: ActiveFiresCollection.find({}).count() }; + } +}); + +// Maps to: /api/v1/status/uptime +apiV1.addRoute('status/uptime', { authRequired: false }, { + get: function get() { + return { ms: new Date() - uptime }; + } +}); + +function getFires(route, full) { + const lat = Number(route.urlParams.lat); + const lng = Number(route.urlParams.lng); + const km = Number(route.urlParams.km); + const { token } = route.urlParams; + try { + checkLatLonDist(km, lat, lng); + check(token, String); + } catch (e) { + return defaultFailParams(e); + } + + const failed = checkAuthToken(token); + if (failed) return failed; + + if (debug) console.log(`Query for fires in ${lat}, ${lng} in ${km} km radius`); + + const fires = ActiveFiresCollection.find({ + ourid: { + $near: { + $geometry: { + type: 'Point', + coordinates: [lng, lat] + }, + $maxDistance: km * 1000, + $minDistance: 0 + } + } + }, { + fields: { + lat: 1, + lon: 1, + when: 1, + scan: 1 + } + }); + + const result = { total: fires.count(), real: countRealFires(fires) }; + + if (!full) { + return result; + } + + // TODO only get real + const firesA = fires.fetch(); + if (firesA.length > 0) { + const union = firesUnion(fires); + const falsePos = whichAreFalsePositives(FalsePositives, union); + const industries = whichAreFalsePositives(Industries, union); + result.fires = firesA; + result.industries = industries.fetch(); + result.falsePos = falsePos.fetch(); + return result; + } + + // otherwise + return { + total: 0, real: 0, fires: [], falsePos: [], industries: [] + }; +} + +// Maps to: /api/v1/fires-in/:lat/:lng/:km +// 100 km max +// Ex: http://127.0.0.1:3000/api/v1/fires-in/token/38.736946/-9.142685/100 +apiV1.addRoute('fires-in/:token/:lat/:lng/:km', { authRequired: false }, { + get: function get() { + return getFires(this, false); + } +}); + +apiV1.addRoute('fires-in-full/:token/:lat/:lng/:km', { authRequired: false }, { + get: function get() { + return getFires(this, true); + } +}); + +// Add mobile user: +// curl -X POST http://localhost:3000/api/v1/users/mobile -d "token: thisAppAutToken" -d "mobileToken: user-mobile-firebase-token" +// Response: +// +// https://docs.meteor.com/api/passwords.html#Accounts-createUser +apiV1.addRoute('mobile/users', { authRequired: false }, { + post: function post() { + const { token, mobileToken, lang } = this.bodyParams; + try { + check(token, String); + check(lang, String); + check(mobileToken, String); + } catch (e) { + return defaultFailParams(e); + } + + const failed = checkAuthToken(token); + if (failed) return failed; + + let username; + + do { + username = Random.id(15); + } while (Meteor.users.find({ username }).count() !== 0); + + // FIXME check valid lang + + const now = new Date(); + // Accounts.createUser({ username: mobileToken, lang: 'FIXME' profile: {} }); + const result = Meteor.users.upsert({ + fireBaseToken: mobileToken + }, { + $set: { + username, + fireBaseToken: mobileToken, + lang, + profile: { }, + createdAt: now, + updatedAt: now + } + }); + + if (debug) { + console.log(this.bodyParams); + console.log(this.urlParams); + console.log(this.queryParams); + } + + const newUser = Meteor.users.findOne({ username }); + + return jsend.success({ + upsertResult: result, + username, + userId: newUser._id, + lang: newUser.lang, + mobileToken: newUser.fireBaseToken + }); + } + +}); + +// max sitance: 100km +apiV1.addRoute('mobile/subscriptions', { authRequired: false }, { + post: function post() { + const { + token, + mobileToken, + lat, + lon, + distance + } = this.bodyParams; + try { + check(token, String); + check(mobileToken, String); + check(lat, Number); + check(lon, Number); + check(distance, Number); + checkLatLonDist(distance, lat, lon); + } catch (e) { + return defaultFailParams(e); + } + + const failed = checkAuthToken(token); + if (failed) return failed; + + const user = Meteor.users.findOne({ fireBaseToken: mobileToken }); + if (!user) return failed; + + const newSubs = {}; + newSubs.location = {}; + newSubs.location.lat = lat; + newSubs.location.lon = lon; + newSubs.distance = distance; + + let result; + try { + result = subscriptionsInsert(newSubs, user._id, 'mobile'); + } catch (e) { + return fail(e); + } + return jsend.success({ subsId: result._str }); + }, + delete: function del() { + const { + token, + mobileToken, + subsId + } = this.bodyParams; + try { + check(token, String); + check(mobileToken, String); + check(subsId, String); + } catch (e) { + return defaultFailParams(e); + } + + const failed = checkAuthToken(token); + if (failed) return failed; + + const user = Meteor.users.findOne({ fireBaseToken: mobileToken }); + if (!user) return failed; + + try { + Subscriptions.remove({ owner: user._id, _id: new Meteor.Collection.ObjectID(subsId) }); + } catch (e) { + return fail(e); + } + + return jsend.success({}); + } +}); diff --git a/imports/api/Subscriptions/methods.js b/imports/api/Subscriptions/methods.js index 618ed21..69456ef 100644 --- a/imports/api/Subscriptions/methods.js +++ b/imports/api/Subscriptions/methods.js @@ -10,30 +10,48 @@ function geo(doc) { }; } +export function subscriptionsInsert(doc, userId, type) { + check(doc, { + location: Match.ObjectIncluding({ lat: Number, lon: Number }), + distance: Number + }); + const newDoc = { + owner: userId, + type, + geo: geo(doc), + ...doc + }; + // console.log(newDoc); + const already = Subscriptions.findOne(newDoc); + if (already) { + throw new Meteor.Error('on-already-subscribed', 'The user is already subscribed to this area'); + } + try { + return Subscriptions.insert(newDoc); + } catch (exception) { + // console.error(exception); + throw new Meteor.Error('500', exception); + } +} + +export function subscriptionsRemove(subscriptionId) { + check(subscriptionId, Meteor.Collection.ObjectID); + + try { + return Subscriptions.remove(subscriptionId); + } catch (exception) { + console.error(exception); + throw new Meteor.Error('500', exception); + } +} + Meteor.methods({ - 'subscriptions.insert': function subscriptionsInsert(doc) { + 'subscriptions.insert': function subscriptionInsertMethod(doc) { check(doc, { location: Match.ObjectIncluding({ lat: Number, lon: Number }), distance: Number }); - const type = 'web'; - const newDoc = { - owner: this.userId, - type, - geo: geo(doc), - ...doc - }; - // console.log(newDoc); - const already = Subscriptions.findOne(newDoc); - if (already) { - throw new Meteor.Error('on-already-subscribed', 'The user is already subscribed to this area'); - } - try { - return Subscriptions.insert(newDoc); - } catch (exception) { - // console.error(exception); - throw new Meteor.Error('500', exception); - } + return subscriptionsInsert(doc, this.userId, 'web'); }, 'subscriptions.update': function subscriptionsUpdate(doc) { check(doc, { @@ -52,15 +70,9 @@ Meteor.methods({ throw new Meteor.Error('500', exception); } }, - 'subscriptions.remove': function subscriptionsRemove(subscriptionId) { + 'subscriptions.remove': function subscriptionsRemoveMethod(subscriptionId) { check(subscriptionId, Meteor.Collection.ObjectID); - - try { - return Subscriptions.remove(subscriptionId); - } catch (exception) { - console.error(exception); - throw new Meteor.Error('500', exception); - } + return subscriptionsRemove(subscriptionId); } }); diff --git a/imports/api/Users/Users.js b/imports/api/Users/Users.js index c6858ae..8911a47 100644 --- a/imports/api/Users/Users.js +++ b/imports/api/Users/Users.js @@ -107,6 +107,7 @@ const schemaUser = new SimpleSchema({ telegramUsername: { type: String, optional: true }, telegramFirstName: { type: String, optional: true }, telegramLanguageCode: { type: String, optional: true }, + fireBaseToken: { type: String, optional: true }, createdAt: defaultCreatedAt, updatedAt: defaultUpdateAt }); diff --git a/imports/startup/server/rest.js b/imports/startup/server/rest.js index 2529a5e..64e8172 100644 --- a/imports/startup/server/rest.js +++ b/imports/startup/server/rest.js @@ -1,161 +1 @@ -/* global Restivus */ -/* eslint-disable import/no-absolute-path */ - -import { Meteor } from 'meteor/meteor'; -import { NumberBetween } from '/imports/modules/server/other-checks'; -import Fires from '/imports/api/Fires/Fires'; -import { check } from 'meteor/check'; -import SiteSettings from '/imports/api/SiteSettings/SiteSettings'; -import ActiveFiresCollection from '/imports/api/ActiveFires/ActiveFires'; -import { countRealFires } from '/imports/api/ActiveFires/server/countFires'; -import { whichAreFalsePositives, firesUnion } from '/imports/api/FalsePositives/server/publications'; -import FalsePositives from '/imports/api/FalsePositives/FalsePositives'; -import Industries from '/imports/api/Industries/Industries'; - -Meteor.startup(() => { - const uptime = new Date(); - - const apiV1 = new Restivus({ - useDefaultAuth: true, - apiPath: 'api', - version: 'v1', - prettyJson: true - }); - - // Generates: POST on /api/users and GET, DELETE /api/users/:id for - // Meteor.users collection - /* apiV1.addCollection(Meteor.users, { - * excludedEndpoints: ['getAll', 'put', 'delete', 'patch', 'get'], - * routeOptions: { - * authRequired: true - * }, - * endpoints: { - * post: { - * authRequired: false - * }, - * delete: { - * roleRequired: 'admin' - * } - * } - * }); */ - - apiV1.addCollection(Fires, { - excludedEndpoints: ['put', 'post', 'patch', 'delete'], - // excludedEndpoints: ['getAll', 'put', 'post', 'patch', 'delete'], - routeOptions: { - authRequired: false - }, - endpoints: { - get: { - action: function getFire() { - return Fires.findOne(new Meteor.Collection.ObjectID(this.urlParams.id)); - } - } - } - }); - - // Maps to: /api/v1/status/last-fire-check - apiV1.addRoute('status/last-fire-check', { authRequired: false }, { - get: function get() { - return SiteSettings.findOne({ name: 'last-fire-check' }); - } - }); - - // Maps to: /api/v1/status/last-fire-detected - apiV1.addRoute('status/last-fire-detected', { authRequired: false }, { - get: function get() { - return ActiveFiresCollection.findOne({}, { sort: { when: -1 } }); - } - }); - - // Maps to: /api/v1/status/last-fires-count - apiV1.addRoute('status/active-fires-count', { authRequired: false }, { - get: function get() { - return { total: ActiveFiresCollection.find({}).count() }; - } - }); - - // Maps to: /api/v1/status/uptime - apiV1.addRoute('status/uptime', { authRequired: false }, { - get: function get() { - return { ms: new Date() - uptime }; - } - }); - - function getFires(route, full) { - const lat = Number(route.urlParams.lat); - const lng = Number(route.urlParams.lng); - const km = Number(route.urlParams.km); - const { token } = route.urlParams; - check(lng, NumberBetween(-180, 180)); - check(lat, NumberBetween(-90, 90)); - check(km, NumberBetween(0, Meteor.isDevelopment ? 1000 : 100)); - check(token, String); - - if (token !== Meteor.settings.private.internalApiToken) { - console.warn(`WARNING: Query for fires in ${lat}, ${lng} in ${km} km radius with wrong token`); - return { error: 'Unauthorized' }; - } - - console.log(`Query for fires in ${lat}, ${lng} in ${km} km radius`); - - const fires = ActiveFiresCollection.find({ - ourid: { - $near: { - $geometry: { - type: 'Point', - coordinates: [lng, lat] - }, - $maxDistance: km * 1000, - $minDistance: 0 - } - } - }, { - fields: { - lat: 1, - lon: 1, - when: 1, - scan: 1 - } - }); - - const result = { total: fires.count(), real: countRealFires(fires) }; - - if (!full) { - return result; - } - - // TODO only get real - const firesA = fires.fetch(); - if (firesA.length > 0) { - const union = firesUnion(fires); - const falsePos = whichAreFalsePositives(FalsePositives, union); - const industries = whichAreFalsePositives(Industries, union); - result.fires = firesA; - result.industries = industries.fetch(); - result.falsePos = falsePos.fetch(); - return result; - } - - // otherwise - return { - total: 0, real: 0, fires: [], falsePos: [], industries: [] - }; - } - - - // Maps to: /api/v1/fires-in/:lat/:lng/:km - // 100 km max - // Ex: http://127.0.0.1:3000/api/v1/fires-in/token/38.736946/-9.142685/100 - apiV1.addRoute('fires-in/:token/:lat/:lng/:km', { authRequired: false }, { - get: function get() { - return getFires(this, false); - } - }); - - apiV1.addRoute('fires-in-full/:token/:lat/:lng/:km', { authRequired: false }, { - get: function get() { - return getFires(this, true); - } - }); -}); +import '../../api/Rest/Rest.js'; diff --git a/package-lock.json b/package-lock.json index 36134d3..1a2b23f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6206,7 +6206,6 @@ "hawk": { "version": "3.1.3", "bundled": true, - "dev": true, "requires": { "boom": "2.10.1", "cryptiles": "2.0.5", @@ -6383,7 +6382,6 @@ "nopt": { "version": "4.0.1", "bundled": true, - "dev": true, "optional": true, "requires": { "abbrev": "1.1.0", @@ -6440,7 +6438,6 @@ "osenv": { "version": "0.1.4", "bundled": true, - "dev": true, "optional": true, "requires": { "os-homedir": "1.0.2", @@ -6449,30 +6446,25 @@ }, "path-is-absolute": { "version": "1.0.1", - "bundled": true, - "dev": true + "bundled": true }, "performance-now": { "version": "0.2.0", "bundled": true, - "dev": true, "optional": true }, "process-nextick-args": { "version": "1.0.7", - "bundled": true, - "dev": true + "bundled": true }, "punycode": { "version": "1.4.1", "bundled": true, - "dev": true, "optional": true }, "qs": { "version": "6.4.0", "bundled": true, - "dev": true, "optional": true }, "rc": { @@ -6489,7 +6481,6 @@ "minimist": { "version": "1.2.0", "bundled": true, - "dev": true, "optional": true } } @@ -6545,31 +6536,26 @@ }, "safe-buffer": { "version": "5.0.1", - "bundled": true, - "dev": true + "bundled": true }, "semver": { "version": "5.3.0", "bundled": true, - "dev": true, "optional": true }, "set-blocking": { "version": "2.0.0", "bundled": true, - "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", "bundled": true, - "dev": true, "optional": true }, "sntp": { "version": "1.0.9", "bundled": true, - "dev": true, "requires": { "hoek": "2.16.3" } @@ -10021,6 +10007,11 @@ } } }, + "jsend": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/jsend/-/jsend-1.0.2.tgz", + "integrity": "sha1-ld99RvvM8fLpVnQ0j5R36TWmyaU=" + }, "jsesc": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", diff --git a/package.json b/package.json index ce8e067..65b675f 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "ismobilejs": "^0.4.1", "jquery": "^2.2.4", "jquery-validation": "^1.17.0", + "jsend": "^1.0.2", "juice": "^4.2.2", "leaflet": "^1.3.1", "leaflet-geodesy": "^0.2.1", diff --git a/test/rest.test.js b/test/rest.test.js new file mode 100644 index 0000000..93cf57c --- /dev/null +++ b/test/rest.test.js @@ -0,0 +1,219 @@ +/* eslint-disable import/no-absolute-path */ +/* eslint-env mocha */ +/* eslint-disable func-names, prefer-arrow-callback */ + +import { chai } from 'meteor/practicalmeteor:chai'; +import { Meteor } from 'meteor/meteor'; +import { HTTP } from 'meteor/http'; +import { Accounts } from 'meteor/accounts-base'; +import Subscriptions from '/imports/api/Subscriptions/Subscriptions'; + +function url(path) { + return `http://127.0.0.1:3000/${path}`; +} + +describe('basic api v1 returns', () => { + it('should return uptime', async done => + HTTP.get(url('api/v1/status/uptime'), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).to.equal(200); + chai.expect(typeof result.data.ms).to.equal('number'); + done(); + })); + + it('should return last-fire', async done => + HTTP.get(url('api/v1/status/active-fires-count'), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).equal(200); + chai.expect(typeof result.data.total).to.equal('number'); + done(); + })); + + const token = Meteor.settings.private.internalApiToken; + + it('should return fires in some location', async done => + HTTP.get(url(`api/v1/fires-in/${token}/38.736946/-9.142685/100`), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).equal(200); + chai.expect(typeof result.data.total).to.equal('number'); + chai.expect(typeof result.data.real).to.equal('number'); + done(); + })); + + it('should return full fires in some location', async done => + HTTP.get(url(`api/v1/fires-in-full/${token}/38.736946/-9.142685/100`), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).equal(200); + chai.expect(typeof result.data.total).to.equal('number'); + chai.expect(typeof result.data.real).to.equal('number'); + chai.expect(typeof result.data.falsePos).to.equal('object'); + chai.expect(typeof result.data.industries).to.equal('object'); + chai.expect(typeof result.data.fires).to.equal('object'); + done(); + })); + + it('should not return full fires with some wrong token', async done => + HTTP.get(url('api/v1/fires-in-full/token/38.736946/-9.142685/100'), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + chai.expect(result.statusCode).equal(200); + done(); + })); + + it('should not return fires with some wrong token', async done => + HTTP.get(url('api/v1/fires-in/token/38.736946/-9.142685/100'), { + data: { + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + chai.expect(result.statusCode).equal(200); + done(); + })); + + it('should not return fires with some wrong distance', async done => + HTTP.get(url(`api/v1/fires-in-full/${token}/38.736946/-9.142685/1100`), (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + chai.expect(result.statusCode).equal(200); + done(); + })); + + const mobileToken = 'user-mobile-firebase-token'; + + let testUserId; + + it('should create mobile users', async (done) => { + // this are removed in the test database but we are testing in dev/ci database + + // Delete the test account if it's still present + // Meteor.users.remove({ fireBaseToken: mobileToken }); + + HTTP.post(url('api/v1/mobile/users'), { + data: { + token, + mobileToken, + lang: 'en' + } + }, (error, result) => { + chai.expect(error, null); + const jsendResult = result.data; + chai.expect(jsendResult.status).equal('success'); + chai.expect(result.statusCode).equal(200); + chai.expect(jsendResult.data.upsertResult.numberAffected).to.equal(1); + chai.expect(jsendResult.data.mobileToken).to.equal(mobileToken); + chai.expect(jsendResult.data.lang).to.equal('en'); + chai.expect(typeof jsendResult.data.username).to.equal('string'); + testUserId = jsendResult.data.userId; + chai.expect(typeof testUserId).to.equal('string'); + done(); + }); + }); + + it('should not create mobile users with wrong token', async (done) => { + // this are removed in the test database but we are testing in dev/ci database + // Delete the test account if it's still present + // Meteor.users.remove({ fireBaseToken: mobileToken }); + + HTTP.post(url('api/v1/mobile/users'), { + data: { + token: 'wrong token', + mobileToken, + lang: 'en' + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + done(); + }); + }); + + let subsId; + + it('should create mobile user subscriptions', async (done) => { + // this are removed in the test database but we are testing in dev/ci database + + // Remove previous subs of this user + // Subscriptions.remove({ owner: testUserId }); + // chai.expect(Subscriptions.find({ owner: testUserId }).count()).to.equal(0); + HTTP.post(url('api/v1/mobile/subscriptions'), { + data: { + token, + mobileToken, + lat: 3.106111, + lon: 53.5775, + distance: 100 + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).equal(200); + const jsendResult = result.data; + chai.expect(jsendResult.status).equal('success'); + subsId = jsendResult.data.subsId; + done(); + }); + }); + + it('should remove mobile user subscriptions', async (done) => { + // this are removed in the test database but we are testing in dev/ci database + + HTTP.del(url('api/v1/mobile/subscriptions'), { + data: { + token, + mobileToken, + subsId + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.statusCode).equal(200); + const jsendResult = result.data; + chai.expect(jsendResult.status).equal('success'); + done(); + }); + }); + + it('should not create mobile user subscriptions with wrong token', async (done) => { + HTTP.post(url('api/v1/mobile/subscriptions'), { + data: { + token: 'wrongOne', + mobileToken, + lat: 3.106111, + lon: 53.5775, + distance: 100 + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + done(); + }); + }); + + it('should not remove mobile user subscriptions with wrong token', async (done) => { + HTTP.del(url('api/v1/mobile/subscriptions'), { + data: { + token: 'wrongOne', + mobileToken, + subsId + } + }, (error, result) => { + chai.expect(error, null); + chai.expect(result.data.status).equal('error'); + done(); + }); + }); +});