node-red-node-web-nodes
Version:
A collection of Node-RED nodes for popular web services.
329 lines (297 loc) • 18.6 kB
JavaScript
/**
* Copyright 2014 IBM Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var should = require("should");
var sinon = require('sinon');
var stravaNode = require("../../strava/strava.js");
var helper = require('../helper.js');
var nock = helper.nock;
describe('Strava node', function() {
beforeEach(function(done) {
helper.startServer(done);
});
afterEach(function(done) {
if(nock) {
nock.cleanAll();
}
try {
helper.unload();
helper.stopServer(done);
} catch (e) {
var errorMessage = "" + e;
errorMessage.should.be.exactly("Error: Not running");
done();
}
});
describe('query node', function() {
it('redirects the user to Strava for authorization', function(done) {
var clientID = 123456789;
var clientSecret = 987654321;
var redirectURI = 'http://localhost:1880/strava-credentials/auth/callback';
var querystring = require("querystring");
var redirectURIQueryString = querystring.escape(redirectURI);
helper.load(stravaNode, [{id:"stravaCredentials1", type:"strava-credentials"},
{id:"stravaNode1", type:"strava", strava: "stravaCredentials1",request:"get-most-recent-activity", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}],
{
"stravaCredentials1" : { // pre-loaded credentials, no need to call OAuth
username: "UserJohn",
access_token: "AN_ACCESS_TOKEN",
cliendID: "A_CLIENT_ID",
redirectURI: "AN_URI",
}
}, function() {
helper.request()
.get('/strava-credentials/auth?node_id=n2&client_id=' + clientID + '&client_secret=' + clientSecret + '&redirect_uri=' + redirectURI)
.expect(302) // expect redirect
.expect(function(res) {
// expect redirect with the right query
try {
res.headers.location.indexOf("https://www.strava.com/oauth/authorize/?client_id=" + clientID + "&redirect_uri=" + redirectURIQueryString + "&response_type=code&state=").should.equal(0);
} catch (err) {
done(err);
}
})
.end(function(err, res) {
if (err) {
return done(err);
}
done();
});
});
});
it('reports an error when the UI doesn\'t supply all credentials', function(done) {
helper.load(stravaNode, [{id:"stravaCredentials1", type:"strava-credentials"},
{id:"stravaNode1", type:"strava", strava: "stravaCredentials1",request:"get-most-recent-activity", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}],
{
"stravaCredentials1" : { // pre-loaded credentials, no need to call OAuth
username: "UserJohn",
access_token: "AN_ACCESS_TOKEN",
cliendID: "A_CLIENT_ID",
redirectURI: "AN_URI",
}
}, function() {
helper.request()
.get('/strava-credentials/auth')
.end(function(err, res) {
if (err) {
return done(err);
}
res.text.should.equal("ERROR: Received query from UI without the needed credentials");
done();
});
});
});
if (nock) { // featues requiring HTTP communication/mocking
/*jshint -W082 */
function doOauthDance(done, matchCsrfToken, return200, serveUserName, serveAccessToken) {
var csrfToken; // required to get and process/pass on the token, otherwise OAuth fails
var clientID = 123456789;
var clientSecret = 987654321;
var redirectURI = 'http://localhost:1880/strava-credentials/auth/callback';
var accessToken = 'AN_ACCESS_TOKEN';
var sessionCode = 'SOME_CODE_FROM_STRAVA';
var querystring = require("querystring");
var redirectURIQueryString = querystring.escape(redirectURI);
var scope = null;
if(return200 === true && serveUserName === true && serveAccessToken === true) {
scope = nock('https://www.strava.com')
.post('/oauth/token', "client_id=" + clientID + "&client_secret=" + clientSecret + "&code=" + sessionCode)
.reply(200, {"access_token":accessToken,"athlete":{"firstname":"John","lastname": "Smith"}});
} else if(return200 === true && serveUserName === true && serveAccessToken === false) {
scope = nock('https://www.strava.com')
.post('/oauth/token', "client_id=" + clientID + "&client_secret=" + clientSecret + "&code=" + sessionCode)
.reply(200, {"athlete":{"firstname":"John","lastname": "Smith"}});
} else if(return200 === true && serveUserName === false){
scope = nock('https://www.strava.com')
.post('/oauth/token', "client_id=" + clientID + "&client_secret=" + clientSecret + "&code=" + sessionCode)
.reply(200, {"access_token":accessToken});
} else {
scope = nock('https://www.strava.com')
.post('/oauth/token', "client_id=" + clientID + "&client_secret=" + clientSecret + "&code=" + sessionCode)
.reply(404, "No tokens found, sorry!");
}
helper.load(stravaNode, [{id:"stravaCredentials1", type:"strava-credentials"},
{id:"stravaNode1", type:"strava", strava: "stravaCredentials1",request:"get-most-recent-activity", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}],
{
"stravaCredentials1" : { // pre-loaded credentials, no need to call OAuth
username: "UserJohn",
access_token: "AN_ACCESS_TOKEN",
cliendID: "A_CLIENT_ID",
redirectURI: "AN_URI",
}
}, function() {
helper.request()
.get('/strava-credentials/auth?node_id=n2&client_id=' + clientID + '&client_secret=' + clientSecret + '&redirect_uri=' + redirectURI)
.expect(function(res) {
try {
csrfToken = res.headers.location.split("&state=n2%3A")[1];
if(matchCsrfToken === false) {
csrfToken = "sorryMismatchingToken";
}
} catch (err) {
done(err);
}
})
.end(function(err, res) {
if (err) {
return done(err);
}
// now call the callback URI as if Strava called it
if(matchCsrfToken === true) {
if(return200 === true && serveUserName === true && serveAccessToken === true) {
helper.request()
.get('/strava-credentials/auth/callback?code=' + sessionCode + '&state=n2:' + csrfToken)
.expect(function(res) {
try {
res.text.indexOf("Successfully authorized with Strava.").should.not.equal(-1); // should succeed
} catch (err) {
done(err);
}
})
.end(function(err, res) {
if (err) {
return done(err);
}
// now call the callback URI as if Strava called it
done();
});
} else if (return200 === true && serveUserName === true && serveAccessToken === false) {
helper.request()
.get('/strava-credentials/auth/callback?code=' + sessionCode + '&state=n2:' + csrfToken)
.end(function(err, res) {
if (err) {
return done(err);
}
res.text.should.equal("Error! Strava node has failed to fetch a valid access token.");
done();
});
}
else if(return200 === true && serveUserName === false){
helper.request()
.get('/strava-credentials/auth/callback?code=' + sessionCode + '&state=n2:' + csrfToken)
.end(function(err, res) {
if (err) {
return done(err);
}
res.text.should.equal("Error! Strava node has failed to fetch the authenticated user\'s name.");
done();
});
} else {
helper.request()
.get('/strava-credentials/auth/callback?code=' + sessionCode + '&state=n2:' + csrfToken)
.end(function(err, res) {
if (err) {
return done(err);
}
res.text.should.equal("Strava replied with the unexpected HTTP status code of 404");
done();
});
}
} else {
helper.request()
.get('/strava-credentials/auth/callback?state=n2:' + csrfToken)
.end(function(err, res) {
if (err) {
return done(err);
}
res.text.should.equal("CSRF token mismatch, possible cross-site request forgery attempt.");
done();
});
}
});
});
}
it('can do oauth dance', function(done) {
doOauthDance(done, true, true, true, true);
});
it('reports csrftoken mismatch', function(done) {
doOauthDance(done, false, true, true, true);
});
it('reports failure if Strava throws an error', function(done) {
doOauthDance(done, true, false, true, true);
});
it('reports failure if Strava doesn\'t serve a user name', function(done) {
doOauthDance(done, true, true, false, true);
});
it('reports failure if Strava doesn\'t serve an access token', function(done) {
doOauthDance(done, true, true, true, false);
});
it('gets the most recent activity details', function(done) {
var activityID = "TEST_ID";
var type = "testType";
var duration = "1000";
var distance = "2000";
var calories = "500";
var startTime = "2014-12-05T13:00:00Z";
var latitude = "51.03";
var longitude = "-1.4";
var title = "aTitle";
var otherData = "otherStuff";
var scope = nock('https://www.strava.com')
.get('/api/v3/athlete/activities')
.reply(200, [{"id":activityID}])
.get('/api/v3/activities/' + activityID)
.reply(200, {"id":activityID,"name":title,"distance":distance,"moving_time":duration,"elapsed_time":duration,"type":type,"start_date":startTime,"start_date_local":startTime,"start_latitude":latitude,"start_longitude":longitude,"calories":calories,"otherData":otherData});
helper.load(stravaNode, [{id:"stravaCredentials1", type:"strava-credentials"},
{id:"stravaNode1", type:"strava", strava: "stravaCredentials1",request:"get-most-recent-activity", wires:[["helperNode1"]]},
{id:"helperNode1", type:"helper"}],
{
"stravaCredentials1" : { // pre-loaded credentials, no need to call OAuth
username: "UserJohn",
access_token: "AN_ACCESS_TOKEN",
cliendID: "A_CLIENT_ID",
redirectURI: "AN_URI",
}
}, function() {
var stravaNode1 = helper.getNode("stravaNode1");
var helperNode1 = helper.getNode("helperNode1");
helperNode1.on("input", function(msg) {
try {
msg.payload.id.should.equal(activityID);
msg.payload.title.should.equal(title);
msg.payload.type.should.equal(type);
msg.payload.duration.should.equal(duration);
msg.payload.distance.should.equal(distance);
msg.payload.calories.should.equal(calories);
msg.payload.starttime.valueOf().should.equal(new Date(Date.parse(startTime)).valueOf());
if(msg.payload.otherData) {
should.fail("otherData should only be passed to msg.data!");
}
msg.location.lat.should.equal(latitude);
msg.location.lon.should.equal(longitude);
msg.data.id.should.equal(activityID);
msg.data.name.should.equal(title);
msg.data.type.should.equal(type);
msg.data.elapsed_time.should.equal(duration);
msg.data.distance.should.equal(distance);
msg.data.calories.should.equal(calories);
msg.data.start_date_local.should.equal(startTime);
msg.data.otherData.should.equal(otherData);
msg.data.start_latitude.should.equal(latitude);
msg.data.start_longitude.should.equal(longitude);
done();
} catch(err) {
done(err);
}
});
stravaNode1.receive({payload:""});
});
});
}
});
});