How to test a Solidity exceptions using Hardhat and Chai? - mocha.js

I'm new working with Solidity. I have a very simple contract that is doing some validation
function withdraw(uint amount) external {
uint balance = wallets[msg.sender];
require(balance >= amount, "Pool: not enough balance");
....
and I want to test it so tried sometime like this
try {
await sut.connect(lp1).withdraw(utils.parseUnits("500000000000"))
} catch(e) {
console.log(e)
}
but it's giving me the following error
Uncaught RuntimeError: abort(Error: VM Exception while processing
transaction: reverted with reason string 'Pool: not enough balance').
Build with -s ASSERTIONS=1 for more info.
It seems that my try/catch is not working. I tried expect(....).to.throw() but the result is the same.
It looks like something breaks at VM level and there is nothing to do from JS. I'm using Hardhat, Typescript and Chai.
Any idea? I was trying to find a solution but nothing show up... which is weird, testing these kind of situations is pretty common.
Thanks

I found a way to do it, probably because I'm using types/chai-as-promised.
const withdraw = sut.connect(lp1).withdraw(utils.parseUnits("500"))
await expect(withdraw).eventually.to.rejectedWith(Error, "VM Exception while processing transaction: reverted with reason string 'Pool: not enough balance'")
The trick is not waiting inside expect, and then using eventually.to.rejectedWith

Try this instead. First install ethereum-waffle with your favorite package manager (yarn xD).
import { solidity } from "ethereum-waffle";
import { use, expect } from "hardhat";
use(solidity);
describe("...", async () => {
it("...", async () => {
await expect(erc721.tokenURI(0)).to.be.revertedWith("ERC721: the token does not exist");
});
}
`

Related

Nightwatch - How to subscribe to CDP Events?

As you may already know, Nightwatch 2 now includes methods for making calls to CDP protocol.
So, I'm trying to capture network responses. I know that the "Network" target has to be enabled and also we have to subscribe a callback to the Network.responseReceived event. I've already done it in another framework tool, but I can't find any related example in the Nightwatch documentation that uses CDP events.
At this point, I don't know which is the method that allows this subscription or where is it, is it in browser.driver or in cdpConnection objects?
This is the code I'm currently trying:
module.exports = {
'#tags': ['njs2-03'],
async myTest (browser) {
await browser.driver.sendAndGetDevToolsCommand('Network.enable')
const cdpConnection = await browser.driver.createCDPConnection('page');
cdpConnection._wsConnection.on('Network.responseReceived', entry => {
console.log('ENTRY >>', entry)
})
await browser.url('https://duckduckgo.com/')
}
}
Any suggestion would be really helpful, Thanks!
I was looking for an answer to a similar problem myself. It appears that it is sometimes much better to analyze the source code of Nightwatch directly. Especially after version 2 was released.
Analysis of the CDP commands eg. the mockNetworkResponse method in the Nightwatch code in the method-mappings.js file give answers.
https://github.com/nightwatchjs/nightwatch/blob/098306cf77d4e380b69ab836231947fe94a12ca0/lib/transport/selenium-webdriver/method-mappings.js
Mind that you are using directly the _wsConnection object. Therefore, that is the message event you are looking for. https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/message_event
Thus, try the following
async myTest (browser) {
await browser.driver.sendAndGetDevToolsCommand('Network.enable')
const cdpConnection = await browser.driver.createCDPConnection('page')
cdpConnection._wsConnection.on('message', message => {
const messageParsed = JSON.parse(message)
if (messageParsed.method === 'Network.responseReceived') {
console.log('DEVTOOLS EVENT PARAMS >>', messageParsed.['params'])
}
})
await browser.url('https://duckduckgo.com/')
}

Axios async/await flow

I'm dipping my toe into the waters of Axios and async/await at the same time, and am trying to understand something about the control flow. Is the following legitimate?
let loading=true;
(async() => {
let response = null;
try {
response = await axios.get('https://whatever.com/api');
} finally {
loading=false;
}
if(response){
//do something with response here
}
})();
That is, can I count on the request to have returned at the point I am accessing the response variable? I appreciate I could guarantee it is by moving it into the 'try' immediately after the axios get, but then I would have to have the loading=false line before it, as well as in 'finally' (or 'catch'). I need to ensure that loading is set to false before any further actions, whether the request succeeds or fails, and I don't want to repeat myself. Maybe there's a better way of doing this?
Edit
Now that you have changed the question, the previous solution will not be working correctly. The issue is that the code inside the IIFE will be executed after everything else is finished, so loading will never be set to false from the perspective of the outside code. (the other code will be executed, and thеn the IIFE. That's because of the event loop). Your best bet is to make the outside code async and await the axios promise.
If you provide the problem details I might be able to help you refactor it.
Previous answer
I need to ensure that loading is set to false before any further actions
Any code after the await is guaranteed to NOT be loading:
(async() => {
let response = await axios.get('https://whatever.com/api');
// the request is finished, the await guarantees that
})();
If you need error handling, you can wrap it in a try/catch:
(async() => {
try {
let response = await axios.get('https://whatever.com/api');
// definitely not loading
}
catch (e) {
// definitely not loading, but an error occurred
}
})();

Xamarin TouchRunner Hangs calling IOS 3rd Party Binding

I have a 3rd party API IOS Binding which I am trying to test (more like an integration test) using TouchRunner.
An example API Method is this -
_client.AuthenticateWithUsername(username, token,
() => { // Success Callback },
() => { // NoConnection Callback },
(obj) => { // Other Error Callback });
The API when called goes off and does some work in the background then eventually makes one of the callbacks above, I would like to control the flow of the unit test using something like -
How can I unit test async methods on the UI Thread with Xamarin iOS TouchRunner
Unfortunately, when I insert the AutoResetEvent code, TouchRunner just hangs and never returns to the GUI.
I have also tried to use a TaskCompletionSource as follows -
public async Task<AuthResponse> AuthenticateUserAsync(string username, string password)
{
TaskCompletionSource<AuthResponse> tcs = new TaskCompletionSource<AuthResponse>();
AuthResponse response = new AuthResponse { Success = false };
LoginResponse loginResponse = await LoginUser(username, password);
_client.AuthenticateWithUsername(username, loginResponse.token,
() =>
{
response.Success = true;
Console.WriteLine("Auth");
tcs.SetResult(response);
},
() => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); },
obj => { tcs.SetResult(response); });
return await tcs.Task;
}
[Test]
public async void AuthenticateUserAsyncTest()
{
var auth = await AuthenticateUserAsync(_username, _password);
Assert.IsTrue(auth.Success);
}
The debugger stepped through fine until the return await tcs.Task, but then results in a similar HUNG runner.
How can I work out why the hang is happening?
As this was not working, I then resorted to code like this -
_client.AuthenticateWithUsername(_username, loginResponse.token,
() =>
{
Assert.Pass("This crashes the runner");
Assert.True(true); // This DOES NOT!
},
() =>
{
// This will crash runner also
Assert.Fail("NoConnection");
},
(InvalidTokenError obj) =>
{
Assert.Fail("InvalidToken" + obj.Description);
},
(ClientError obj) =>
{
Assert.Fail("ClientError" + obj.Description);
});
As you can see, the flow ends up (understandably), run test, runs client call, end of test method completes which shows test as success, then the callback returns and the asserts get called, which crash the app, which we assume is because the runner has already completed the test, why one assert works and other crashes I do not know.
So,
Am I approaching this the right way?
Could something be happening in the 3rd Party API that will cause these approaches to hang? and how would I debug that?
Thanks #Nkosi, that is a good suggestion, I forgot to add that during my original testing that when I ran the code with async Task rather than void I got an immediate block from TouchRunner without even adding any other code other than the API call! This was a red flag I suppose, but using async void "seemed" to allow "standard" async testing, so I progressed and then ended up in the loop above.
As TouchRunner has not been updated in a very long time I have just spent time re-creating the test project using XUnit after various suggestions to try it in the forums and on stack.
https://github.com/xunit/devices.xunit - runners for Xamarin IOS + Android
https://xunit.github.io/docs/comparisons - to port NUnit syntax
Some other useful links are -
https://xunit.github.io/docs/getting-started-devices.html
https://gregshackles.com/testing-xamarin-apps-getting-started-with-xunit/
https://oren.codes/2014/07/10/getting-started-with-xunit-for-xamarin/
RESULT: I am very pleased to say all the above code now works for both the TaskCompletionSource and the AutoResetTask scenarios
I can now safely test my event based API :)
I just wanted to ensure other users are aware of this.
Thanks for your help.
One observation is that the test should be async Task and not async void ie
public async Task AuthenticateUserAsyncTest() {
//...code removed for brevity.
}
async void is a fire and forget so any exceptions thrown wont happen in the current context so they wont be caught.
Reference Async/Await - Best Practices in Asynchronous Programming

Amazon IAP Plugin for Xamarin - crash when using TaskCompletionSource

I'm trying to implement a wrapper for the Amazon IAP Plugin for Xamarin. It uses an event based system in the following way:
You can initiate method calls and listen for events. Method calls initiate requests, some of which return a response. Events are asynchronous system-generated messages that are sent in response to method calls to return the requested data to you.
See more here
My goal is to wrap this event based system into some API which allows me to use the plugin with tasks, so I can use the async-await syntax. To achieve that I'm using the TaskCompletionSource like in the following example:
public async Task<bool> GetProductInfoAsync(params string[] productIds)
{
var iapService = AmazonIapV2Impl.Instance;
var tcs = new TaskCompletionSource<bool>();
var skus = new SkusInput { Skus = productIds.ToList() };
var requestId = iapService.GetProductData(skus).RequestId;
GetProductDataResponseDelegator delegator = null;
delegator = new GetProductDataResponseDelegator(response =>
{
if(response.Id == requestId) {
var result = GetResultFromResponse(response);
tcs.SetResult(result);
//iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate);
}
});
iapService.AddGetProductDataResponseListener(delegator.responseDelegate);
return await tcs.Task;
}
This code seems to work fine if the method gets called once, but if it gets called two times in a row the app crashes immediately and the only thing printed to the console is the following message..
[mono] Unhandled Exception:
[mono] System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
..which kinda makes no sense at all.
So is there something obvious I'm missing here? Or could it be a bug from the plugin?
I have created a repository with the code above so you can reproduce the problem. It's my playground, so please ignore the whole structure of the project and just focus on the classes AmazonIAPService and MainActivity.
Hint 1:
The commented line //iapService.RemoveGetProductDataResponseListener(delegator.responseDelegate); causes also a crash with the same message but already at the first call of the method.
Hint 2:
The AmazonIAPService contains a commented method which uses await Task.Delay(TimeSpan.FromMilliseconds(1)) and solves the problem from above in a very hacky way which I really don't like.
Problem seems to be that those functions have to run asynchronously. Also mentioned here in the doc. So once you run those functions synchronously somehow they throw exception, i dont what is happening in the library but your hacky solution is the actual solution for that. If you write the function as below. it also works.
PurchaseResponseDelegator delegator = null;
delegator = new PurchaseResponseDelegator(async response =>
{
await Task.Run(() =>
{
if (response.RequestId == requestId)
{
var result = GetPurchaseEventHandler(response);
var sucess = taskCompletionSource.TrySetResult(result);
context.RemovePurchaseResponseListener(delegator.responseDelegate);
}
} );
});
// Register for an event
context.AddPurchaseResponseListener(delegator.responseDelegate);
One other exception I had despite the async-await solution, somehow, it always throws exception for the line taskCompletionSource.SetResult(result); for PurchaseUpdates functions only. if i use instead this line var sucess = taskCompletionSource.TrySetResult(result); it works fine

Logout in case of a bad request

thanks for the amazing job you've done with AOR, this have been a great source of inspiration for me, especially in how to implement a proper redux store.
My question :
If I make a bad request, (code 400), AOR log me out of the app.
How can I prevent this and instead show a notification to my user ?
You can follow the instructions to achieve it:
import { AUTH_ERROR } from 'admin-on-rest';
export default (type, params) => {
if (type === AUTH_ERROR) {
// As I noticed, this code is executed for any error, not only for auth-one.
// So process the error as you wish (show message), don't log out.
}
// Then resolve the promise.
return Promise.resolve();
};

Resources