Chai should be bignumber equal doesn't work - testing

I am requireing chai like this
const BigNumber = web3.BigNumber;
require('chai').use(require('chai-bignumber')(BigNumber)).should();
and during test
let balance = await contract.balanceOf(accountToReceive);
should.be.bignumber.eql(countToSend); //works fine
ownerBalance = await contract.balanceOf(owner);
should.be.bignumber.equal(settings.initialSupply.mul(utils.toBN(10).pow(_decimals)).sub(countToSend)); // gives en error
Error code is
AssertionError: expected <BN: 94e47b8d68171533ffff9c> to equal <BN: 94e47b8d68171533ffff9c>
+ expected - actual
at Context.<anonymous> (test/MOS.test.js:79:33)
at processTicksAndRejections (node:internal/process/task_queues:96:5)

You can use the chai plugin for BN.js which should work with should.
See its usage at:
https://www.chaijs.com/plugins/chai-bn/

I also faced the same issue with my code. I found a good solution which works for me.
Try the following:
require('chai').use(require('chai-as-promised')).should();
.use(bnChai(web3.utils.BN));

As I've mentioned in update, you can use JS Bigint, do all your math with it and then just pass it to BN constructor as a string, like
utils.toBN((1000000n * 5748553687688487n).toString())

I faced the same problem, the answer is simple
just use the expect function of chai with to.be.bignumber.equal
Example:
const { expect } = chai;
const { BN } = require('#openzeppelin/test-helpers');
const exp_val = new BN(); // any value
expect(contract.method()).to.be.bignumber.equal(exp_value);

Related

Calling specific methods on a Solana Solidity program

I've built a simple smart contract to run on the Ethereum blockchain and I'm trying to replicate some of it's behavior on Solana. After making some slight changes I've managed to compile the program with Solang targeting Solana, but I'm not sure how to go about calling the methods; there doesn't seem to be a great wealth of documentation or examples on this. For example, if my program were written as follows:
contract Example {
function foo(...) { ... }
function bar(...) { ... }
}
How would I specify a call to foo vs a call to bar? Furthermore, how would I encode the arguments for these method calls?
Currently my approach is to use the #solana/buffer-layout library to encode my arguments as a struct, starting with lo.u8('instruction') to specify the method call (in the example case, I assume 0 would refer to foo and 1 would refer to bar). I've taken this approach based on looking at the source code for #solana/spl-token (specifically this file and it's dependencies) but I'm not sure if it will work for a program compiled using Solang, and the buffer layout encoding has been throwing an unexpected error as well:
TypeError: Blob.encode requires (length 32) Uint8Array as src
The code throwing this error is as follows:
const method = lo.struct([
lo.u8('instruction'),
lo.seq(
lo.blob(32),
lo.greedy(lo.blob(32).span),
'publicKeys',
),
])
const data = Buffer.alloc(64); // Using method.span here results in an error, as method.span == -1
method.encode(
{
instruction: 0,
publicKeys: [firstPublicKey, secondPublicKey],
},
data,
);
While this type error seems obvious, it doesn't line up with the sample code in the solana-labs/solana-program-library repository. I'm pretty sure this problem has to do with my use of lo.seq() but I'm not sure what the problem is.
Is my approach to this correct besides this type error, or is my approach fundamentally wrong? How can I call the intended method with encoded arguments? Thank you for any help.
There's a better library for you to use, #solana/solidity, which has a Contract class to encapsulate calls on the contract.
For example, in your case, you could do:
const { Connection, LAMPORTS_PER_SOL, Keypair } = require('#solana/web3.js');
const { Contract, Program } = require('#solana/solidity');
const { readFileSync } = require('fs');
const EXAMPLE_ABI = JSON.parse(readFileSync('./example.abi', 'utf8'));
const PROGRAM_SO = readFileSync('./example.so');
(async function () {
console.log('Connecting to your local Solana node ...');
const connection = new Connection('http://localhost:8899', 'confirmed');
const payer = Keypair.generate();
console.log('Airdropping SOL to a new wallet ...');
const signature = await connection.requestAirdrop(payer.publicKey, LAMPORTS_PER_SOL);
await connection.confirmTransaction(signature, 'confirmed');
const program = Keypair.generate();
const storage = Keypair.generate();
const contract = new Contract(connection, program.publicKey, storage.publicKey, EXAMPLE_ABI, payer);
await contract.load(program, PROGRAM_SO);
console.log('Program deployment finished, deploying the example contract ...');
await contract.deploy('example', [true], program, storage);
const res = await contract.functions.foo();
console.log('foo: ' + res.result);
const res2 = await contract.functions.bar();
console.log('bar: ' + res2.result);
})();
Example adapted from https://github.com/hyperledger-labs/solang#build-for-solana
More information about the package at https://www.npmjs.com/package/#solana/solidity

How does node-redis method setex() convert Buffer to string?

I'm using node-redis and I was hoping that someone could help me to figure out how this library converts Buffer to string. I gzip my data before I store it in redis with node-gzip and this call returns Promise<Buffer>
const data = JSON.stringify({ data: 'test' });
const compressed = await gzip(data, { level: 9 });
I tested following 2 approaches of saving buffer data into redis
Without .toString() - I pass the Buffer to the library and it will take care of the conversion
const result = await redisClient.setex('testKey', 3600, compressed);
and with .toString()
const result = await redisClient.setex('testKey', 3600, compressed.toString());
When I try these 2 approaches I don't get the same value saved in redis. I tried to use different params for .toString() to match the output of 1) but it didn't work
Reason why I need the saved value in 1) format is that I'm matching value format that what one of php pages generates
My code is working fine without .toString() but I would like to know how node-redis handles it internally
I've tried to find the answer in the source code and to debug and step into library calls but I didn't find the answer that I was looking for and I hope that someone can help me with this
It looks like happens in utils.js file:
utils.js
if (reply instanceof Buffer) {
return reply.toString();
}
Also use the proper options (i.e. return_buffers):
node-redis README
redis.createClient({ return_buffers: true });

_.forEach out of order

I have some code within an async function:
let listOfResults = [];
_.forEach(listOfProducts, async function (product) {
const result = await calc.calculateProductScore(product);
const response = await mutateDB.sendProductScoreData(result.productId, result);
await listOfResults.push(response);
})
if (listOfResults.length >= 1) {
return "yay";
} else {
return "boo";
}
Given that there's three items in the listOfProducts array, stepping through the debugger in VS Code when this executes this happens in the following order:
the line const result = await calc.calculateProductScore(product); is executed three times
the if statement (which returns boo as nothing has been pushed onto the listOfResults array)
the line const response = await mutateDB.sendProductScoreData(result.productId, result); is executed three times
the line await listOfResults.push(response); is executed three times
I can see in lodash forEach documentation https://lodash.com/docs/4.17.15#forEach it mentions that "iteration order is not guaranteed" - should I not be using lodash then for this type of code block? How can I use Lodash and guarantee that order of code execution?
I understand if I want to keep things in order I need to use a for (const product of listOfProducts) loop....
Please check this good article, it's talking about the Array.forEach, but I think it should help.
https://codeburst.io/javascript-async-await-with-foreach-b6ba62bbf404

Javascript code not working after upgrade to karate 0.9.3

The below function in feature files was working for version 0.9.2. Upgraded to 0.9.3 and this gives error : javascript function call failed: Index: 0.0, Size: 0. Code below:
var cnd = ['test1','test2'];
function set_filter(arg)
{
var i;
var filter = {filterValues:[]};
for(i=0;i<arg.length;i++)
{
filter.filterValues[i] = arg[i];
}
return filter;
}
set_filter(cnd)
Also i was earlier able to push values in a javascript array using below, but this has also stopped working in 0.9.3. Get error:javascript function call failed: TypeError: arr.push is not a function
var arr = [];
arr.push('test1','test2');
Try the scenario below that works in 0.9.2 but reports error (mentioned above) in 0.9.3
Scenario: JS test
* def filter_template =
"""
function() {
var filter_params = {
filterValues:[]
};
return filter_params;
}
"""
* def template = call filter_template
* def filter_condition = ['test1','test2']
* def setFilter =
"""
function(arg) {
var i;
var filter = arg.filter_template;
for(i=0;i<arg.condition.length;i++)
{
filter.filterValues[i] = arg.condition[i];
}
return filter;
}
"""
* def getFilter = call setFilter { filter_template: '#(template)', condition: '#(filter_condition)' }
* print getFilter
Help is much appreciated.
We've made the JS conversions stricter, everything will be a Java collection behind the scenes. If you make this change, things will start working:
filter.filterValues.set(i, arg.condition.get(i));
The same goes for push() - use add() or karate.appendTo(varname, value) instead.
My strong recommendation is don't use too much of JS logic especially loops. Karate has functions such as map(), forEach() and repeat() to solve for these needs. Please refer the docs here: https://github.com/intuit/karate#loops
You will thank me later because it will make your scripts easier to understand and maintain. One reason why this is needed is to pave the way for us to change the JS engine in the future.

angular.copy issue with Jasmine Test case

Request you to help me find the solution of below issue.
I have one function and corresponding Jasmine test case, like the one written below.
If I use angular.copy (and I have to use this only) in my function, Jasmine test case fails and error shown is expected 'originalValue' to equal 'newValue'.
If I use var obj = param1 (and not angular.copy) then Jasmine test case executes successfully.
I have to use angular.copy and at the same time, want jasmine test case to pass. Please help.
function
function func(param1, param2, condition)
{
var obj = angular.copy(param1);
if(condition){
obj.prop = param2;
}
}
jasmine test case
it('xxxx', function(){
var param = {'prop': ''};
var obj = {'prop': 'orignialValue'};
func(param, 'newValue', true);
expect(obj.prop).toEqual('newValue');
});
The example you provided is incorrect.
it('xxxx', function(){
var obj = {'prop': 'orignialValue'};
func(param, 'newValue', true);
expect(obj.prop).toEqual('newValue');
});
param is not defined over here, this should be obj I assume.
Since you use angular.copy, obj.prop is not added by ref to param1.
I found the solution. Instead of doing angular.copy inside the function func, I am first doing angular.copy and then passing this copied object to function func as its first parameter (param1).