Cucumber Js callback issue? or feature issue? - javascript

I'd like to write a feature like this:
Scenario: new Singleton create
When a new, unmatchable identity is received
Then a new tin record should be created
And a new bronze record should be created
And a new gold record should be created
which would tie to steps like this:
defineSupportCode(function ({ Before, Given, Then, When }) {
var expect = require('chai').expect;
var chanceGenerator = require('./helpers/chanceGenerator')
var request = require('./helpers/requestGenerator')
let identMap;
// reset identMap before each scenario
Before(function () {
identMap = [];
});
// should generate a valid identity
// persist it in a local variable so it can be tested in later steps
// and persist to the db via public endpoint
When('a new, unmatchable identity is received', function (callback) {
identMap.push(chanceGenerator.identity());
request.pubPostIdentity(identMap[identMap.length-1], callback);
});
// use the local variable to retrieve Tin that was persisted
// validate the tin persisted all the props that it should have
Then('a new tin record should be created', function (callback) {
request.pubGetIdentity(identMap[identMap.length-1], callback);
// var self = this;
// request.pubGetIdentity(identMap[identMap.length-1], callback, () => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
// request.pubGetIdentity(identMap[identMap.length-1], (callback) => {
// console.log('never gets here...');
// self.callback();
// callback();
// });
});
The issue that I'm having is that I can't do anything in the Then callback. That is where I'd like to be able to verify the response has the right data.
Here are relevant excerpts from the helper files:
var pubPostIdentity = function (ident, callback) {
console.log('pubIdentity');
var options = {
method: 'POST',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
},
body: JSON.stringify(ident)
};
console.log('ident: ', ident);
request(options, (err, response, body) => {
if (err) {
console.log('pubPostIdentity: ', err);
callback(err);
}
console.log('pubPostIdentity: ', response.statusCode);
callback();
});
}
// accept an identity and retrieve from staging via identity public endpoint
var pubGetIdentity = function (ident, callback) {
console.log('pubGetIdentity');
var options = {
method: 'GET',
url: 'http://cucumber.utu.ai:4020/identity/' + ident.platform + '/' + ident.platformId,
headers: {
'X-Consumer-Custom-Id': ident.botId + '_' + ident.botId
}
};
request(options, (err, response) => {
if (err) {
console.log('pubGetIdentity: ', err);
callback(err);
}
console.log('pubGetIdentity: ', response.body);
callback();
});
}
Something that we are considering as an option is to re-write the feature to fit a different step definition structure. If we re-wrote the feature like this:
Scenario: new Singleton create
When a new, unmatchable 'TIN_RECORD' is received
Then the Identity Record should be created successfully
When the Identity Record is retreived for 'tin'
Then a new 'tin' should be created
When the Identity Record is retreived for 'bronze'
Then a new 'bronze' should be created
When the Identity Record is retreived for 'gold'
Then a new 'gold' should be created
I believe it bypasses the instep callback issue we are wrestling with, but I really hate the breakdown of the feature. It makes the feature less readable and comprehensible to the business.
So... my question, the summary feature presented first, is it written wrong? Am I trying to get step definitions to do something that they shouldn't? Or is my lack of Js skills shining bright, and this should be very doable, I'm just screwing up the callbacks?

Firstly, I'd say your rewritten feature is wrong. You should never go back in the progression Given, When, Then. You are going back from the Then to the When, which is wrong.
Given is used for setting up preconditions. When is used for the actual test. Then is used for the assertions. Each scenario should be a single test, so should have very few When clauses. If you want, you can use Scenario Outlines to mix several very similar tests together.
In this case, is recommend to take it back to first principles and see if that works. Then build up slowly to get out working.
I suspect in this case that the problem is in some exception being thrown that isn't handled. You could try rewriting it to use promises instead, which will then be rejected on error. That gives better error reporting.

Related

Initializing web workers

It seems the only communication from host to worker is postMessgage and onmessage. If the worker requires some dynamic initialization (as in constructor, here: regular expression to use later), what is the best way to do this?
A possibility would be to let data be an object, and have e.g. an action parameter, and to check this on every run. This seems a bit kludgey.
The communication channel between different agents is very low-level, but you can easily build a higher level communication based on that. I'd use objects to communicate different "events":
{ event: "init", data: [/d/] }
Based on these events, you can create different events to represent e.g. function calls and their response:
{ event: "call-init", data: [/d/] } // >>>
{ event: "return-init", data: ["done"] } // <<<
Then you can build a wrapper around that, that sends and handles the response, something like:
async function call(name, ...args) {
channel.send("call-" + name, args);
return await cannel.once("return-" + name);
}
channel.init = call.bind(null, "init");
Then your code turns into something along the lines of:
const channel = new Channel(worker);
await channel.init(/d/);
await channel.doOtherStuff();
That's just to give you the basic idea.
A more ad-hoc approach than Jonas' solution abuses the Worker's name option: You can e.g. pass the regex string in this name and use it later:
test.js
var a = new Worker("worker.js", {name: "hello|world"})
a.onmessage = (x) => {
console.log("worker sent: ")
console.log(x)
}
a.postMessage("hello")
worker.js
var regex = RegExp(this.name);
onmessage = (a) => {
if (regex.test(a.data)) {
postMessage("matches");
} else {
postMessage("no match for " + regex);
}
}

Request-promise-native not chaining 'then' as expected on working API calls

Useage of 'request-native-promise' not correctly chaining to it's subsequent 'then' and 'catch' handlers.
My Protractor Test
// There's a bunch of other imports here
import { browser } from "protractor";
const RequestPromise = require('request-promise-native');
describe('spec mapper app', () => {
let specMapperPage: SpecMapperPage;
let specMapperFVRPage: SpecMapperFieldsValuesReviewPage;
let loginLogoutWorkflow: LoginLogoutWorkflow;
let apiToken: LoginToken;
let tokenUtil: TokenUtil;
let projectRecordsToBeDeleted = [];
let requestHandler;
let logger = new CustomLogger("spec mapper app");
let speccyEndpoints = new SpeccyEndpoints();
beforeAll( () => {
logger.debug("Before All")
loginLogoutWorkflow = new LoginLogoutWorkflow();
loginLogoutWorkflow.login();
tokenUtil = new TokenUtil();
tokenUtil.getToken().then((token:LoginToken) => {
apiToken = token;
requestHandler = new SpeccyRequestHandler(apiToken);
});
});
describe('import/export page', () => {
it('TC2962: I'm a test case', () => {
let testLogger = new CustomLogger("TC2955");
// Test Var setup
... // removed for brevity
// Test Setup
browser.waitForAngularEnabled(false);
// Setup the record to be on the mapper page
let body = speccyEndpoints.generateRevitPostBody(PROJECT_ID, fileName);
requestHandler.postToSpeccy(speccyEndpoints.DELITE_REVIT_POST_URI, body).then((response) => { // EDIT: removed non-existant argument "rejection"
// --> The then handler the promise is STILL not resolving into
// Only made it here like once
console.log("Response is: ");
console.log(response);
// I got this to work once, but now it's not
console.log("Response body is: ");
console.log(response.body);
}).catch(error => {
// --> The catch handler is ALSO NOT resolving
console.log("catch handler executed!");
console.log(error);
});
});
});
});
The test case where things are going wrong. My console.log("Response is: "); is NOT being outputted. I'm not getting error messages as to why.
My Speccy Request Handler Wrapper Class
import * as RequestPromise from "request-promise-native";
import {LoginToken} from "../testObjects/LoginToken";
import {CustomLogger} from "../logging/CustomLogger";
export class SpeccyRequestHandler {
_baseAPIURL = 'http://myapi.net/';
_options = {
method: '',
uri: '',
auth: {
'bearer': ''
},
headers: {
'User-Agent': 'client'
},
"resolveWithFullResponse": true,
body: {},
json: true
};
_logger;
constructor(apiToken: LoginToken) {
this._options.auth.bearer = apiToken.idToken;
this._logger = new CustomLogger(SpeccyRequestHandler.name);
}
getOptions() {
return this._options;
}
postToSpeccy(uri:string, body?) {
this._options.method = 'POST';
this._options.uri = this._baseAPIURL + uri;
if(body) {
this._options.body = body;
}
return RequestPromise(this._options);
}
getFromSpeccy(uri) {
this._options.method = 'GET';
this._options.uri = this._baseAPIURL + uri;
return RequestPromise(this._options);
}
}
This is my Request Handler specific to one of my APIs, the Speccy one, and has some custom aspects to it in the URL and the token passing.
Sources
Request-Promise-Native Github Page
Request-Promise Github page, documentation location
Update Number 1
After the fix #tomalak brought to my attention, my console.log's in the .then(... handler were being executed, the first 5-ish times after I changed over to this, I was getting a roughly 150+ line console log of the response object that contained a body of response I would expect from my request URI. I even got the body out by using response.body. I thought things were fixed and I wasn't using my logger that logs out to file, so I lost my proof. Now when I run this test and this request I do not go into the .then(... handler at all. I'm also not going into the catch. My request is working though, as my resource is created when the post endpoint is hit. Any further suggestions are appreciated.
What would cause something to work sometimes and not others? My only thought is maybe the generic post name in my request handler wasn't being used in lieu of another method higher up the build chain being caught.
Update Number 2
Removed a bunch of stuff to shorten my question. If you need more clarification, ask and I'll add it.
It ended up being a timeout on the end of the API. My response was simply taking too long to get back to me. It wasn't failing, so it never went into the catch. And I had it working at one point because the response taking so long is due to an overabundance of a certain resource in our system in particular. I thought it was me and something I had written wrong. Long story short, suspect your system, even if you think it's perfect or if your devs swear up and down nothing could be broken.
Also, the request-debug module was a nice thing to have to prove that other endpoints, such as the rest testing endpoints at https://jsonplaceholder.typicode.com/ , do work with your code.

Parse.com - secure sending data with javascript SDK

I'm building right now simple game with Angular JS and Parse.com cloud as my database.
My goal is in the and of the game, to store user score inside Parse cloud.
But how can i do this securly, when anyone can get access to my Parse keys, becouse they are visible in my js file, and simply recreate Parse Object with some fake data, and then store it in my database ?
ACL's it's not the point in this particular case - right now i just turn of write acl before save, to prevent users from changing they scores before save.
In my game i don't have any Parse Users - i want to all peaople play my game, without logging in.
What do you think about idea to make 'fake' user like in first answer in this post ( becouse Anonymous anonymous can't be create in JS parse SDK ), and then track the session and the user ?
Is it even helpful in my case ?
Maybe i should make some check in Cloude Code - like comparison Cookies or local storage data before saving in Parse ( it will make cheating in game harder but still possible ) ?
Below i present my whole service to show you what is all about:
angular.module('Parsedb', [])
.provider('Parsedbmanager', function() {
this.$get = function($q, $http) {
// new Parse constructor
var ParseHighScore = Parse.Object.extend('ParseHighScore');
// create new obj
var parseHighScore = new ParseHighScore();
this.parseInit = function() {
Parse.initialize('myKey', 'myKey');
};
this.setParsedb = function(newScore) {
// set val
parseHighScore.set('HighScore', newScore);
// save score to cloud
parseHighScore.save(null, {
success: function (parseHighScore) {
// protect from change saved obj
var acl = new Parse.ACL();
acl.setPublicReadAccess(true);
acl.setPublicWriteAccess(false);
parseHighScore.setACL(acl);
return parseHighScore.save();
},
error: function (parseHighScore, error) {
console.log('Failed to create new object, with error code: ' + error.message);
}
});
};
this.getParsedb = function() {
// need q to get this asynch
var deferred = $q.defer();
var query = new Parse.Query(ParseHighScore);
query.limit(5);
query.descending("HighScore");
query.find({
success: function(results) {
console.log("Successfully retrieved " + results.length + " scores.");
// resolve, if you have results
deferred.resolve(results);
},
error: function(error) {
deferred.reject(error.message);
}
});
return deferred.promise;
};
return this;
};
});
If you let the user to write to db then there will always be a situation where user can change data .. i think all you can do is, to abstract it from user

Meteor running a Method asynchronously, using meteorhacks:npm package

I'm trying to use the Steam Community (steamcommunity) npm package along with meteorhacks:npm Meteor package to retreive a user's inventory. My code is as follows:
lib/methods.js:
Meteor.methods({
getSteamInventory: function(steamId) {
// Check arguments for validity
check(steamId, String);
// Require Steam Community module
var SteamCommunity = Meteor.npmRequire('steamcommunity');
var community = new SteamCommunity();
// Get the inventory (730 = CSGO App ID, 2 = Valve Inventory Context)
var inventory = Async.runSync(function(done) {
community.getUserInventory(steamId, 730, 2, true, function(error, inventory, currency) {
done(error, inventory);
});
});
if (inventory.error) {
throw new Meteor.Error('steam-error', inventory.error);
} else {
return inventory.results;
}
}
});
client/views/inventory.js:
Template.Trade.helpers({
inventory: function() {
if (Meteor.user() && !Meteor.loggingIn()) {
var inventory;
Meteor.call('getSteamInventory', Meteor.user().services.steam.id, function(error, result) {
if (!error) {
inventory = result;
}
});
return inventory;
}
}
});
When trying to access the results of the call, nothing is displayed on the client or through the console.
I can add console.log(inventory) inside the callback of the community.getUserInventory function and receive the results on the server.
Relevant docs:
https://github.com/meteorhacks/npm
https://github.com/DoctorMcKay/node-steamcommunity/wiki/CSteamUser#getinventoryappid-contextid-tradableonly-callback
You have to use a reactive data source inside your inventory helper. Otherwise, Meteor doesn't know when to rerun it. You could create a ReactiveVar in the template:
Template.Trade.onCreated(function() {
this.inventory = new ReactiveVar;
});
In the helper, you establish a reactive dependency by getting its value:
Template.Trade.helpers({
inventory() {
return Template.instance().inventory.get();
}
});
Setting the value happens in the Meteor.call callback. You shouldn't call the method inside the helper, by the way. See David Weldon's blog post on common mistakes for details (section Overworked Helpers).
Meteor.call('getSteamInventory', …, function(error, result) {
if (! error) {
// Set the `template` variable in the closure of this handler function.
template.inventory.set(result);
}
});
I think the issue here is you're calling an async function inside your getSteamInventory Meteor method, and thus it will always try to return the result before you actually have the result from the community.getUserInventory call. Luckily, Meteor has WrapAsync for this case, so your method then simply becomes:
Meteor.methods({
getSteamInventory: function(steamId) {
// Check arguments for validity
check(steamId, String);
var community = new SteamCommunity();
var loadInventorySync = Meteor.wrapAsync(community.getUserInventory, community);
//pass in variables to getUserInventory
return loadInventorySync(steamId,730,2, false);
}
});
Note: I moved the SteamCommunity = Npm.require('SteamCommunity') to a global var, so that I wouldn't have to declare it every method call.
You can then just call this method on the client as you have already done in the way chris has outlined.

Why does creating and deleting an Azure Table fail?

I am wondering why trying to run the following test suite fails when I try to delete the table I have stored entities in. The error I get is the following
1) Azure Storage cloud storage operations "after all" hook:
Error: The specified resource does not exist. RequestId:3745d709-fa5e-4a2b-b517-89edad3efdd2
Time:2013-12-03T22:26:39.5532356Z
If I comment out the actual insertion of data it fails every other time, and if I try to do the insertion of data it fails every time with an additional "The table specified does not exist.".
For the first case this seems to indicate that there is some kind of delay in the table creation, so in every other test it is successful, and for the second case it seems to indicate that even though my callbacks are being called after table creation, the table(s) still aren't ready for data insertion.
The test suite and associated code looks like this:
describe('cloud storage operations', function () {
var storage;
before(function (done) {
this.timeout(5000);
storage = AzureStorage.usingTable('TEST', done);
});
after(function (done) {
storage.deleteTable(done);
});
it('should store without trouble', function (done) {
storage.save(factory.createChangeSet()).then(done, done);
});
});
... // snipped from azure.js
var AzureStorage = function (storageClient, tableName, callback) {
assert(storageClient && tableName && partitionKey, "Missing parameters");
this.storageClient = storageClient;
this.tableName = tableName;
var defaultCallback = function (err) { if (err) { throw error; } };
this.storageClient.createTableIfNotExists(this.tableName, function () {
callback();
} || defaultCallback);
};
AzureStorage.usingTable = function (tableName, callback) {
return new AzureStorage(
azure.createTableService(accountName, accountKey)
, tableName
, callback
);
};
AzureStorage.prototype.deleteTable = function (callback) {
this.storageClient.deleteTable(this.tableName, callback);
};
I've hit this using the c# library as well but I'm pretty sure the error message indicated the table could not be created because an operation was still in process for a table of the same name. Thinking of the backend supporting storage, it makes sense that it would not be instant. The table needs to be removed from the 3 local replicas as well as the replicas in the paired data center.
With that kind of async operation, it is going to be challenging to build up an tear them down fast enough for tests.
A workaround might be to increment a value appended to the "TEST" table name that would be unique to that test run.

Categories