Mocking LUIS response with LuisRecognizer not working - node.js

I am trying to mock calls to LUIS via nock, which uses the LuisRecognizer from botbuilder-ai. Here is the relevant information.
The bot itself is calling LUIS and getting the result via const recognizerResult = await this.dispatchRecognizer.recognize(context);. I grabbed the actual result as below:
{"text":"I want to look up my order","intents":{"viewOrder":{"score":0.996454835},"srStatus":{"score":0.0172454268},"expediteOrder":{"score":0.0108480565},"escalate":{"score":0.007967358},"qna":{"score":0.00694736559},"Utilities_Cancel":{"score":0.005627355},"manageProfile":{"score":0.004953466},"getPricing":{"score":0.001781322},"Utilities_Help":{"score":0.0007197641},"getAvailability":{"score":0.0005667514},"None":{"score":0.000321137835}},"entities":{"$instance":{}},"sentiment":{"label":"negative","score":0.171873689},"luisResult":{"query":"I want to look up my order","topScoringIntent":{"intent":"viewOrder","score":0.996454835},"intents":[{"intent":"viewOrder","score":0.996454835},{"intent":"srStatus","score":0.0172454268},{"intent":"expediteOrder","score":0.0108480565},{"intent":"escalate","score":0.007967358},{"intent":"qna","score":0.00694736559},{"intent":"Utilities.Cancel","score":0.005627355},{"intent":"manageProfile","score":0.004953466},{"intent":"getPricing","score":0.001781322},{"intent":"Utilities.Help","score":0.0007197641},{"intent":"getAvailability","score":0.0005667514},{"intent":"None","score":0.000321137835}],"entities":[],"sentimentAnalysis":{"label":"negative","score":0.171873689}}}
For the sake of brevity, I'll just call this "recognizerResult" in the below. I'm successfully intercepting the API call in my test file with nock, with the configuration as follows:
nock('https://westus.api.cognitive.microsoft.com')
.post(/.*/)
.reply(200,{recognizerResult});
I've tried returning both as a JSON object and a string, though I'm almost certain this needs to be JSON object as shown (I'm mocking a call to QnA maker with the same approach that is working). When I run this test via mocha, I get the following error:
TypeError: Cannot read property 'replace' of undefined
at LuisRecognizerV2.normalizeName (node_modules\botbuilder-ai\src\luisRecognizerOptionsV2.ts:96:21)
at luisResult.intents.reduce (node_modules\botbuilder-ai\src\luisRecognizerOptionsV2.ts:104:31)
at Array.reduce (<anonymous>)
at LuisRecognizerV2.getIntents (node_modules\botbuilder-ai\src\luisRecognizerOptionsV2.ts:102:32)
at LuisRecognizerV2.<anonymous> (node_modules\botbuilder-ai\src\luisRecognizerOptionsV2.ts:81:27)
at Generator.next (<anonymous>)
at fulfilled (node_modules\botbuilder-ai\lib\luisRecognizerOptionsV2.js:11:58)
at process._tickCallback (internal/process/next_tick.js:68:7)
I've looked at the code in question within the luisRecognizerOptionsV2.ts file, but can't see where there's an issue. The replace is part of normalizing the intent name, which is there to replace unsupported characters with an "_". The bot runs normally when deployed to Azure (and locally), and the tests work without mocking the call. However, I really want to be able to test this without making actual LUIS calls. Any ideas why I am getting this error and how to fix?
For reference, here is the mock to QnA Maker that is working, though note that I'm using a simple REST call for that instead of the recognizer.
nock('https://myqnaservicename.azurewebsites.net')
.post(/.*/)
.reply(200, {"answers": [{"questions": ["I need an unrecognized utterance for testing"], "answer": "I can hear you now!", "score": 28.48, "id": 1234}]});

The issue is that your {recognizerResult} is what gets saved to const recognizerResult, but is not what gets returned by that API call.
It takes a lot of digging to find it all, but a V2 LUIS client gets the API response, then converts it into recognizerResult.
You've got a few options for "fixing" this:
Set a breakpoint in that node_modules\botbuilder-ai\src\luisRecognizerOptionsV2 file on that const result = line and grab luisResult.
Use something like Fiddler to record the actual API response and use that
Write it manually
For reference, you can see how we do this in our tests:
nock()
Recorded response
You can see that our nock() returns response.v2, which does not contain .topScoringIntent, which is what it's looking for, which is why the error is throwing.
Specifically, the mock response needs to be just the v2/luisResults attributes. In other words, when using the luisRecognizer, the response set in nock needs to be
.reply(200,{ "query": "Sample query", "topScoringIntent": { "intent": "desiredIntent", "score":1}, "entities":[]});
If you look at the test data linked above, there are other attributes in the actual response. But this is the minimum required response if you are just trying to get topIntent to test routing. If you needed other attributes you could add them, e.g. you could add everything within v2 as in this file or some of the more involved files with things like multiple intents.

Related

Body is not getting parsed in GET method

I am using mockoon for API simulation. I created 2 routes there with method GET and its body contains(responds with) JSON object. I noticed that my express app is not able to parse one of the routes. But the route that has JSON object in body which contains ARRAY is getting parsed. I tested both routes with Express(by console.log) and in chrome browser(I have JSON formatter extension) and it is behaving the same meaning response that does not contain ARRAY is not getting parsed but the response with array is getting parsed(behaving normally). Let me show the screenshots:
Express(by console.log):
With array:
Without array:
Chrome(JSON Formatter extension):
With array(extension is able to parse):
Without array(extension is not able to parse):
I tried adding Header(Content-Type: application/json) to the route in mockoon. But still, I am not aware of what is going on here. Someone please explain
The express code:
const iabs_client = await axios.get(
"http://localhost:3001/iabs-client
);
Here is the route created in Mockoon(without array inside JSON):
P.S mockoon is a program that creates endpoints in localhost, useful for API simulation when developing front-end without having backend yet
The trailing comma after "something" is not valid JSON. Edit your Mockoon body to remove the comma and it should work.

What is the proper way to return a JSON object to Alexa Smart Home or end AWS Lambda in NodeJS?

I have seen three ways to return a JSON object or end a Lambda function. My trigger is Smart Home Alexa.
I am using now is context.succeed(response_JSON);This one works for me. Even if this instructions is inside a nested function. The whole Lambda ends and return the response_JSON to Smart Home Alexa.
I have seen in other blogs that say callback(response_error,response_JSON). This one did not work for me. It did not return anything to Smart Home.
Others just uses the return response_JSON. I have not used this one.
I am using now is context.succeed(response_JSON);This one works for me. Even if this instructions is inside a nested function. The whole Lambda ends and return the response_JSON to Smart Home Alexa.
context.succeed()/fail() causes the Lambda function to terminate immediately. However, I have not seen this documented in the context object docs, so it may get deprecated in later Node versions (?).
I have seen in other blogs that say callback(response_error,response_JSON). This one did not work for me. It did not return anything to Smart Home.
This one probably doesn't work for you because by default Node.js waits for the event loop to be empty before executing the callback statement. This may be due to open network/database connection. As per the doc, set the context.callbackWaitsForEmptyEventLoop variable to false to send the response right away.
Others just uses the return response_JSON. I have not used this one.
This should be used with async handlers. Read more about async and non-async handlers here: https://docs.aws.amazon.com/lambda/latest/dg/nodejs-handler.html

Twilio function event parameter does not contain value. Example: Event.name object is undefined

I'm facing an issue where when my twilio funciton is called, when i pass in two function parameters as shown below, they cannot be accessed from within the twilio function. The error says that event.name is undefined. but im passing a parameter called name through the funciton like shown below. So after passing a paremeter like this (edit: here im passing test but its the same output for test as well) :
I get this output
my code is as below :
FIXED! Sorry it was totally a problem on my end, I had configured the SMS and the Call for the same number, that was creating a bit of a issue.
What do you get back when you log out the entire event object stringified like so; console.log(JSON.stringify(event))

Triggering Google Cloud function using console

I am trying to use the google cloud console to test a cloud function. Below is a snippet.
exports.requestCreated = functions.firestore
.document('users/{userId}/requests/{requestId}')
.onWrite((change, context) => {
// execute operation
});
I have tried all sorts of combination of JSON data. E.g.
{"userId":"Xl86pqOpF9T2MAn12p24OJAfYJW2","requestId":"abc1234"}
But I keep getting the following statement in logs:
Request created by {userId}
The actual userId is not being read from the JSON data in the console. Can you help?
This is not a problem with the execution of the cloud function. It's a problem with hardcoding the string.
'users/{userId}/requests/{requestId}' is a hardcoded string. Node.js will not automatically replace {userId} with the value of the variable userId.
Following this previous SO post, try something like this using template strings:
`users/${userId}/requests/${requestId}`
Please note it is surrounded by backticks (`), not single quotes (').
This assumes you already have a userId and requestId variables defined. You must restructure your cloud function like this to retrieve that data. Notice that the specific variable values must be extracted from the event variable.
Thank you, Nareddyt. The function is for Firestore, and the way it is written right now checks if a new document is created under the collection requests. I tried replacing the string as you suggested, but as you pointed out, it requires these variables to be defined. I do not quite understand how to restructure the cloud function because the syntax I have used is how event detection is suggested in the Firestore documentation. My function currently works in its entirety, but testing it is a major pain. I have to go through my mobile app and do the whole userflow to test this function. I am new to Node.js and any guidance would be appreciated.

How can I test multiple pages in a single Intern test

Is it possible for a single functional test to handle 2 different pages? For example, when I execute the following test:
return this.remote
.get(testPage)
.waitForElementByCssSelector('.alfresco-core-Page.allWidgetsProcessed', 5000)
.elementByCss('#UNIT_TEST_MODEL_FIELD>DIV.control>TEXTAREA')
.type(testData)
.end()
.elementByCss("#LOAD_TEST_BUTTON")
.click()
.sleep(2000)
.waitForElementByCssSelector('.alfresco-core-Page.allWidgetsProcessed', 5000)
.elementByCss("#DD1")
.click()
.end();
I get the following error:
Test main - firstTest - Test1 FAILED on chrome 31.0.1650.57 on LINUX:
Error: Cannot call method 'apply' of undefined
TypeError: Cannot call method 'apply' of undefined at null.<anonymous> (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/lib/util.js:108:10)
at /home/dave/ScratchPad/ShareInternTests/node_modules/intern/lib/wd.js:769:29
at signalListener (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:37 :21)
at signalWaiting (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:28 :4)
at resolve (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:19 2:5)
at signalDeferred (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:81 :15)
at signalListener (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:52 :6)
at signalWaiting (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:28 :4)
at resolve (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:19 2:5)
at signalDeferred (/home/dave/ScratchPad/ShareInternTests/node_modules/intern/node_modules/dojo/Deferred.js:81 :15)
Because of the framework that I'm testing I need to load a "bootstrap" test page which I write the test data into and then POST it to persist a test model into the HTTP session which is then rendered in a resultant page.
However, whilst the first part of the test works fine (I see the test data entered and the next page is submitted, I seem to get the error on the 2nd .waitForElementByCssSelector call. I've tried various permutations but can't get this to work.
If I run a completely second test on the second page then this works fine, but ideally I'd like it all captured within a single test.
Is what I'd like to do possible or do I have to break it into separate tests?
Please try using setImplicitWaitTimeout and use the elementBy* methods instead of using waitForElementByCssSelector. It is more efficient, and should work properly.
A second option is to make sure you call end before the second waitForElementByCssSelector call; it looks like there is a defect in the way the waitForElement methods are called, where the #LOAD_TEST_BUTTON element is being used as the context for the call but waitForElement accepts no context.

Resources