websocket onMessage gets invoked only once in mochajs test - unit-testing

// global
var ws = null;
var msgCount = 0;
var onMessageHandler = null;
// test #1 - this test passes successfully
describe('connect to wsserver test', function() {
it('should successfully connect to wsserver', function(done) {
this.timeout(0);
ws = new FayeWebSocket.Client('wss://server.com', null, {
headers: {
"authToken": "someToken"
}
});
ws.on('open', function() {
done();
});
ws.on('message', function(msg) {
if (onMessageHandler && typeof onMessageHandler === 'function') {
onMessageHandler(msg);
}
});
ws.on('close', function(event) {
console.log('closing websocket!');
});
ws.on('error', function(err) {
console.log("error!: " + err.message);
});
});
});
// test #2 - this test blocks indefinitely
describe('send request and get back 3 response messages', function() {
it('should get back 3 response messages from the wsserver', function(done) {
this.timeout(0);
// this function is called ONLY once, although the server is sending 3 messages
onMessageHandler = function(msg) {
msgCount++;
console.log(msg);
if (msgCount >= 3) {
done();
}
}
var sendThisRequest = {
'some': 'json',
'with': 'some key/value pairs'
}
// this line sends a request to the wsserver
ws.send(JSON.stringify(sendMsg));
});
});
I'm trying to write some basic unit tests to test my websocket apis. These mocha tests are simulating the client, NOT the websocket server.
In the first test I just connect to the websocket server using a websocket, this test passes successfully.
In the second test I'm sending a request to the server ( from ws.send(message) ), the server does get this request correctly, does some processing and sends 3 websocket messages to the client. (Looking at the server logs, I can say that this part is working fine on the server)
The test should complete after getting the 3 messages and some expect(something).to.equal(something) assertions
I haven't been able to figure out so far why the 2nd and 3rd messages are never picked up by the onMessageHandler(). I've tried placing the ws.on('message',function(msg){..}) block at different places in the code but to no avail.
If someone could point me in the right direction for this problem, that would be great. Thanks

I think you are not able to do streaming of data with current unit test with mocha.
ws.send(JSON.stringify(sendMsg));
You are calling this statement only once which means only one time data is streamed. You need to call it multiple time to check n time streaming.

This code works correctly. There was some problem with the inputs that I was passing to the server. Sorry!

Related

Postman test script - how to call an api twice to simulate 409 error

I am trying to run a few automated testing using the Postman tool. For regular scenarios, I understand how to write pre-test and test scripts. What I do not know (and trying to understand) is, how to write scripts for checking 409 error (let us call it duplicate resource check).
I want to run a create resource api like below, then run it again and ensure that the 2nd invocation really returns 409 error.
POST /myservice/books
Is there a way to run the same api twice and check the return value for 2nd invocation. If yes, how do I do that. One crude way of achieving this could be to create a dependency between two tests, where the first one creates a resource, and the second one uses the same payload once again to create the same resource. I am looking for a single test to do an end-to-end testing.
Postman doesn't really provide a standard way, but is still flexible. I realized that we have to write javascript code in the pre-request tab, to do our own http request (using sendRequest method) and store the resulting data into env vars for use by the main api call.
Here is a sample:
var phone = pm.variables.replaceIn("{{$randomPhoneNumber}}");
console.log("phone:", phone)
var baseURL = pm.variables.replaceIn("{{ROG_SERVER}}:{{ROG_PORT}}{{ROG_BASE_URL}}")
var usersURL = pm.variables.replaceIn("{{ROG_SERVICE}}/users")
var otpURL = `${baseURL}/${phone}/_otp_x`
// Payload for partner creation
const payload = {
"name": pm.variables.replaceIn("{{username}}"),
"phone":phone,
"password": pm.variables.replaceIn("{{$randomPassword}}"),
}
console.log("user payload:", payload)
function getOTP (a, callback) {
// Get an OTP
pm.sendRequest(otpURL, function(err, response) {
if (err) throw err
var jsonDaata = response.json()
pm.expect(jsonDaata).to.haveOwnProperty('otp')
pm.environment.set("otp", jsonDaata.otp)
pm.environment.set("phone", phone);
pm.environment.set("username", "{{$randomUserName}}")
if (callback) callback(jsonDaata.otp)
})
}
// Get an OTP
getOTP("a", otp => {
console.log("OTP received:", otp)
payload.partnerRef = pm.variables.replaceIn("{{$randomPassword}}")
payload.otp = otp
//create a partner user with the otp.
let reqOpts = {
url: usersURL,
method: 'POST',
headers: { 'Content-Type': 'application/json'},
body: JSON.stringify(payload)
}
pm.sendRequest(reqOpts, (err, response) => {
console.log("response?", response)
pm.expect(response).to.have.property('code', 201)
})
// Get a new OTP for the main request to be executed.
getOTP()
})
I did it in my test block. Create your normal request as you would send it, then in your tests, validate the original works, and then you can send the second command and validate the response.
You can also use the pre and post scripting to do something similar, or have one test after the other in the file (they run sequentially) to do the same testing.
For instance, I sent an API call here to create records. As I need the Key_ to delete them, I can make a call to GET /foo at my API
pm.test("Response should be 200", function () {
pm.response.to.be.ok;
pm.response.to.have.status(200);
});
pm.test("Parse Key_ values and send DELETE from original request response", function () {
var jsonData = JSON.parse(responseBody);
jsonData.forEach(function (TimeEntryRecord) {
console.log(TimeEntryRecord.Key_);
const DeleteURL = pm.variables.get('APIHost') + '/bar/' + TimeEntryRecord.Key_;
pm.sendRequest({
url: DeleteURL,
method: 'DELETE',
header: { 'Content-Type': 'application/json' },
body: { TimeEntryRecord }
}, function (err, res) {
console.log("Sent Delete: " + DeleteURL );
});
});
});

Next JS How can i set cookies in an api without errors?

Next JS. I am trying to set some cookies in my /api/tokencheck endpoint. Here is a very simplified version of the code:
import { serialize } from 'cookie';
export default (req, res) => {
/* I change this manually to simulate if a cookie is already set */
let cookieexists = 'no';
async function getToken() {
const response = await fetch('https://getthetokenurl');
const data = await response.json();
return data.token;
}
if (cookieexists === 'no') {
getToken().then((token) => {
res.setHeader('Set-Cookie', serialize('token', token, { path: '/' }));
});
return res.status(200).end();
} else {
return res.status(200).end();
}
};
I have tried a ton of variations as to where to put my return.res.status... code, and tried many different ways to return a success code, but depending on where I put the code I variously end up with either of the following errors:
"API resolved without sending a response for /api/checkguestytoken, this may result in stalled requests."
or
"unhandledRejection: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client"
I seem to have some gap in my knowledge about how the API works in Next JS because I cannot figure out how to just run the async function, get a result, set a couple of cookies and then exit with a 200. Could someone please tell me what I'm doing wrong?

Sequelize model only updates when I log out then log back in

So basically my issue is that when a user is logged in they can take a test. When the test is passed a user is given points and their level is updated using a put route. Once the put route has run the user is redirected back to their homepage where their stats are displayed. If I look in Postman after the request is made I can see the changes. However, the get route on the user's homepage is still displaying as the previous data. Only when I log out then log back in does the user object actually update on the app.
the get request is called in the document. ready function for the user's page like so...
$(document).ready(() => {
// This file just does a GET request to figure out which user is logged in
// and updates the HTML on the page
$.get("/api/user_data").then(data => {
console.log(data)
$(".user-name").text(data.username);
$(".first-name").text(data.first);
$(".last-name").text(data.last);
$(".email").text(data.email);
$(".xp").text(data.points);
});
the put route is in a function that runs when the last question of the test is answered. It looks like this...
function endGame() {
console.log("END OF GAME SCORE: " + score);
$(".quiz-container").css("display", "none");
console.log(data.points);
console.log(data.level);
var addPoints = score * 100 + data.points;
var newLevel = data.level + 1;
$.ajax({
url: "/api/user_data",
method: "PUT",
data: {
id: data.id,
level: newLevel,
point: addPoints
},
error: function(req, err) {
console.log(err)
},
success: function(res, err) {
window.location.replace("/members");
}
}).then(result => {
console.log("user info updated");
console.log(result);
window.location.replace("/members");
});
}
As you can see the user is redirected over to the "members" page which is where the get request is sent on the document being ready. I'm pretty new so any help would be greatly appreciated.
also here is the db.sync method I had been working with force true and force false now i just have...
db.sequelize.sync({}).then(() => {
app.listen(PORT, () => {
console.log(
"==> 🌎 Listening on port %s. Visit http://localhost:%s/ in your browser.",
PORT,
PORT
);
});
});

Can't push data to Firebase from within an Alexa Skill hosted on AWS Lambda

I have a database in Firebase to which I'm trying to write some data from within my Alexa Skill. The Node.js code for that skill sits inside an AWS Lambda function and when that code is run I want to push some data to Firebase.
I've tested the code that connects to and pushes to Firebase outside of Lambda and it works exactly as expected. Below is that code:
var firebase = require('firebase');
firebase.initializeApp({
databaseURL: 'https://myapp.firebaseio.com',
serviceAccount: './myapp.json',
});
var cart = firebase.database().ref('/cart');
console.log(cart);
cart.push( {
item: 'apples', quantity: '1', amount: '0'
}, function(error) {
if (error) {
console.log("Data could not be saved." + error);
} else {
console.log("Data saved successfully.");
}
});
This same code doesn't push anything to the database instance when executed from within the Lambda function. I read online that Lambda timeout limit could be a reason for this, so I increased the timeout limit to a minute, and it still doesn't run as expected. I've also tried using the Firebase REST API instead of their Node.js SDK and that didn't work either. What is the correct way to push data to Firebase from within AWS Lambda?
I think I know why this happens, I had a similar issue and this is what I've done.
If you want to write some date into your database you need to make sure that you don't call this.emit(*****) until you are done. As soon as you return the response to the user the Thread gets closed and your information doesn't get saved.
The easiest way to solve this problem is to return the response to the user once you get confirmation that the information has been saved.
In case of Firebase something like this:
function writeUserData(userId) {
// Get a key for a new Post.
var userKey = db.ref('users/').push().key;
var reference = db.ref('users/' + userKey).set({
user: userId
});
reference.then(() => {
alexa.emit(':ask', "Works");
},
(err) => {
alexa.emit(':ask', "Does not work");
});
}
I couldn't get anything saved until I started doing it like this.
Hope it helps.
I've run into this too and the only way I've figured out how to get it to work is to delay the lambda callback function in the handler. Try this and let me know if it works.
var firebase = require('firebase');
firebase.initializeApp({
databaseURL: 'https://myapp.firebaseio.com',
serviceAccount: './myapp.json',
});
exports.handler = (event, context, callback) => {
var cart = firebase.database().ref('/cart');
console.log(cart);
cart.push( {
item: 'apples', quantity: '1', amount: '0'
setTimeout(()=>{
callback(null, 'success');
},2000);
}, function(error) {
if (error) {
console.log("Data could not be saved." + error);
setTimeout(()=>{
callback(error);
},2000);
} else {
console.log("Data saved successfully.");
setTimeout(()=>{
callback(null, 'success');
},2000);
}
});
}

How can I unit test the request response cycle in Node.js?

For example suppose I have the following
app.get('/', function(req, res) {
var ip;
if(req.headers['x-forwarded-for']){
ip = req.headers['x-forwarded-for'];
}
else {
ip = req.connection.remoteAddress;
}
});
I would like to unit test to see whether ip is being properly retrieved. One way is as follows
function getIp(req) {
var ip;
if(req.headers['x-forwarded-for']){
ip = req.headers['x-forwarded-for'];
}
else {
ip = req.connection.remoteAddress;
}
return ip;
}
app.get('/', function(req, res) {
var ip = getIp(req);
});
Now I have a function getIp that I can unit test. However I'm still stuck. How can I feed a simulated req object into getIp?
I would just write integration-tests for that. Node.js is fast enough for that. Especially when you use something like Mocha's watch-mode. You could use something like superagent or request to perform http requests.
There is also something like for example nock to mock out your http requests. Although I have never used it because integration-tests test the real thing and are fast enough for my tast.
I'd recommend using mocha to write your unit tests in which case you'll use 'request' as your http client. But the simplest way to get started is to use the following:
var http = require('http');
//Change to the ip:port of your server
var client = http.createClient(3000, 'localhost');
var request = client.request('GET', '/',
{'host': 'localhost'});
request.end();
request.on('response', function (response) {
console.log('STATUS: ' + response.statusCode);
console.log('HEADERS: ' + JSON.stringify(response.headers));
response.setEncoding('utf8');
response.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});