Data-driven Testing with Jest - mysql

Need to automate tests with the values retrieve from the database. Also, the same test is needed to be run with a set of data.
I use the Jest Testing framework and have to use it.test() to test the data. The issue is the test file first checks the array that I am passing to it.test() and initially, it is empty. Please check the attached code.
import "jest";
describe('Document Training test suit', function(this:any){
let docTrainingPage: DocumentTraining;
let driver: any;
let valuesArray:Array<any>=[];
var i=0;
beforeAll(async()=>{
const driver = await new ConfigBrowser();
this.driver = driver;
docTrainingPage = new DocumentTraining(this.driver);
await docTrainingPage.connectToDB();
valuesArray = await docTrainingPage.retrieveValues();//Retrieve value from database
});
describe('Should not save the document', async() => {
it.each(valuesArray )('Should NOT savedocuments with INVALID values',async() => {
const A= JSON.stringify(valuesArray[i].AAA);
const B= JSON.stringify(valuesArray[i].BBB);
const C= JSON.stringify(valuesArray[i].CCC);
const D= JSON.stringify(valuesArray[i].DDD);
const E= JSON.stringify(valuesArray[i].EEE);
await docTrainingPage.enterValues(A, B, C, D, E);
const testResult = await docTrainingPage.verifyValidDocTrainingSuccessful();
expect(testResult).toBeTruthy();
i++;
});
})
afterAll(()=>{
docTrainingPage.endConnection();
setTimeout(() => process.exit(), 1000)
});
})
I tried debugging this code. It goes to beforeAll first at the beginning. But then its go to it and check whether any valid tests are there. At this point, the array is not loaded.
Error
● Test suite failed to run
Your test suite must contain at least one test.

Related

How can I start a new transaction on an existing Gnosis Safe from my react app?

Currently I have a React app which connects to a Metamask wallet. On clicking a 'Transfer' button, it transfers some ethers from the connected Metamask wallet to the one input by the user. All of this works fine and is pretty straightforward. It looks something like this (I am using ethers.js):
// fetch signer from connected metamask account
const signer = new ethers.providers.Web3Provider(ethereum).getSigner()
const contract = new ethers.Contract(contractAddress, ERC20ABI, signer)
// create a new transaction - that's it! so simple!
const txResponse = await contract.transfer(_recipientWalletAddress, utils.parseUnits(_userinput).toString())
Now, I want the same functionality but instead of transferring from the connected Metamask wallet, I want to connect to an existing Gnosis Safe and transfer funds from the safe to a recipient. I already have a test Gnosis Safe set up on gnosis-safe.io over Goerli Testnet with 3 owners. I have the Safe address. I have the addresses of the owners.
In my app, I have connected my app to the test Gnosis Safe using WalletConnect.
Now, I want that I click a button, enter a recipient address and submit the form. This should create a new transaction on the test Gnosis Safe. And then on the Gnosis Safe, the owners can approve/reject the created transaction. I am struggling with this part.
This is what I've tried
import Safe from '#gnosis.pm/safe-core-sdk'
import { ethers, utils } from 'ethers'
import EthersAdapter from '#gnosis.pm/safe-ethers-lib'
// have all addresses here
const gnosisSafeAddress = '0xsafexaddress'
const gnosisSafeOwners = ['0xsafexownerx1', '0xsafexownerx2', '0xsafexownerx3']
// create a ethers provider over goerli network
const provider = new ethers.providers.JsonRpcProvider(`https://goerli.infura.io/v3/${infuraAPIKey}`);
// create signer using the provider and private key of the one of the gnosis safe owner
const signer = new ethers.Wallet(PRIVATE_KEY_OF_SAFE_OWNER_1, provider);
// create connection to safe using the gnosis safe address
const ethAdapter = new EthersAdapter({ ethers, signer });
const safeSdk = await Safe.create({ ethAdapter, safeAddress: gnosisSafeAddress });
// create a transaction
const transaction = {
to: _recipientWalletAddress,
value: utils.parseUnits(_userinput).toString(),
data: '0x',
};
const safeTransaction = await safeSdk.createTransaction(transaction);
This does not work at all. I logged every step and every variable in between is empty objects. Kindly help me understand what I am doing wrong. I am extremely confused reading the Gnosis SDK repos.
I don't want to deploy a test Gnosis Safe programmatically. I don't want to create a Safe app. I just want to create a transaction, programmatically, on Gnosis Safe (a wallet) to transfer funds, like I do for metamask.
There are not many good resources on how to interact with Safes from react. I suggest trying to write a script in node to grok the flow first. I created a script here that deploys and sends a transaction from the safe
to do the same thing in react (without using the transaction relayer service) you can do the following
import React, { useEffect, useState } from 'react'
import { Button } from "#nextui-org/react";
import { ethers } from "ethers";
import { useSigner } from 'wagmi'
import EthersAdapter from '#gnosis.pm/safe-ethers-lib'
import Safe, { SafeFactory } from '#gnosis.pm/safe-core-sdk'
import { SafeTransactionDataPartial, TransactionResult, SafeTransaction } from '#gnosis.pm/safe-core-sdk-types'
const threshold: number = 1;
const owners = ['0x0533F9d586ABd3334a0E90cA162602D6574F0493']
const safe = () => {
const [gnosisLoaded, setGnosisLoaded] = useState<boolean>(false)
const [safeDeployed, setSafeDeployed] = useState<boolean>(false)
const [safeAddress, setSafeAddress] = useState<string>('')
const { data: signer } = useSigner()
const [safeSdk, setSafeSdk] = useState<Safe | null>(null)
const [safeFactory, setSafeFactory] = useState<SafeFactory | null>(null)
const deploySafe = async () => {
const safeSdk = await safeFactory?.deploySafe({ safeAccountConfig: { owners, threshold } })
if (!safeSdk) return
setSafeSdk(safeSdk)
setSafeAddress(safeSdk.getAddress())
setSafeDeployed(true)
}
const sendSafeTransaction = async () => {
const to = await signer?.getAddress()
const transaction: SafeTransactionDataPartial = {
to,
value: "1",
data: '0x',
};
const safeTransaction: SafeTransaction = await safeSdk?.createTransaction({ safeTransactionData: transaction });
const executeTxResponse: TransactionResult = await safeSdk?.executeTransaction(safeTransaction)
await executeTxResponse.transactionResponse?.wait()
}
useEffect(() => {
if (!signer) return
const setupGnosis = async () => {
const ethAdapter = new EthersAdapter({ ethers, signer })
const safeFactory = await SafeFactory.create({ ethAdapter })
setSafeFactory(safeFactory)
setGnosisLoaded(true)
}
setupGnosis()
}, [signer])
return (
<div>
{gnosisLoaded ? <div>gnosis loaded</div> : <div>gnosis loading...</div>}
{safeDeployed ? <div>safe deployed: {safeAddress}</div> : <div>safe not deployed</div>}
<Button disabled={!gnosisLoaded} onClick={() => deploySafe()}>
Deploy Safe
</Button>
<Button disabled={!safeDeployed} onClick={() => sendSafeTransaction()}>Send Transaction</Button>
</div>
)
}
export default safe

How to verify message in wallet connect with ethers primarily on ambire wallet?

I am trying to sign a message with wallet connect using ethers, but I am facing an issue when verifying the message with ambire wallet, it's not returning any response.
const signMessage = async () => {
try {
console.log("started");
// 1.] create a provider
const walletConnectProvider = new WalletConnectProvider({
infuraId: "3cd774e14cf34ff78167908f8377051c", // Required
// qrcode: true
});
// 2.] enable provider
await walletConnectProvider.enable();
// console.log(walletConnectProvider.wc.accounts[0]);
let rawMessage = "Hello World";
let rawMessageLength = new Blob([rawMessage]).size;
let message = ethers.utils.toUtf8Bytes(
"\x19Ethereum Signed Message:\n" + rawMessageLength + rawMessage
);
message = ethers.utils.keccak256(message);
var params = [
walletConnectProvider.wc.accounts[0],
message,
];
// 3.] sign message
const provider = new providers.Web3Provider(walletConnectProvider);
const signer = provider.getSigner();
let signature = await signer.signMessage(message);
console.log("signature", signature);
// 4.] verify message
let verified = await ethers.utils.verifyMessage(message, signature);
console.log("verified", verified);
} catch (err) {}
};
there are a couple of things you additionally need:
You need to pass the original message (before prefix) to signer.signMessage, in other words the rawMessage: await signer.signMessage(rawMessage) - because the wallet (no matter if it's Ambire or not) will add the prefix
In order to support smart wallets like Ambire, Gnosis Safe, Argent and others, you need to implement EIP 1271.
In JS (warning: not tested), this will look somewhat like this:
const signerAddr = await signer.getAddress();
if (provider.getCode(signerAddr) === '0x') {
// Regular RSV sig verification
verified = signerAddr === (await ethers.utils.verifyMessage(message, signature));
} else {
// Smart contract wallet (EIP 1271) verification: see https://eips.ethereum.org/EIPS/eip-1271 for more info
const EIP1271ABI = ['function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue)'];
const EIP1271MagicValue = '0x1626ba7e';
const signerEIP1271Contract = new ethers.Contract(signerAddr, EIP1271ABI, provider);
const rawMessageLength = new Blob([rawMessage]).size;
const message = ethers.utils.toUtf8Bytes(
"\x19Ethereum Signed Message:\n" + rawMessageLength + rawMessage
);
const messageHash = ethers.utils.keccak256(message);
verified = EIP1271MagicValue === (await signerEIP1271Contract.isValidSignature(messageHash, signature));
}
NOTE: We, the Ambire team, are currently working on a comprehensive guide on how to verify all styles of signature (EIP1271, EIP712, 712+1271, regular), which will hopefully be linked by the ethers.js documentation.
EDIT: We've published a library that makes this a whole lot easier, please check it out: https://github.com/AmbireTech/signature-validator/ - we recommend that you use that

How do I read environment variables in my Appwrite functions?

In the Appwrite console, I'm adding a test environment variable to pass into function...
In my function code (NodeJs) index.js, I'm logging out the value of above variable...
I save the code and use the Appwrite CLI (createTag) to push/publish the code.
Then in Appwrite console, I activate the new function then I execute it and I see this in log...
Clearly I'm missing something but I'm searching the Appwrite docs and I don't see it.
What am I doing incorrectly?
Thank you for helping :-)
Ok looks like as of this post, this is a bug in the Appwrite web UI code. You can do this 2 ways right now. You can set the environment vars in code or you can use the Appwrite CLI. I ended up putting the CLI commend in my NodeJs package.json scripts for quick easy access.
Here are both ways that worked for me...
appwrite functions create --functionId=regions_get_all --name=regions_get_all --execute=[] --runtime=node-16.0 --vars={ 'LT_API_ENDPOINT': 'https://appwrite.league-tracker.com/v1', 'LT_PROJECT_ID': '61eb...7e4ff', 'LT_FUNCTIONS_SECRET': '3b4b478e5a5576c1...ef84ba44e5fc2261cb8a8b3bfee' }
const sdk = require('node-appwrite');
const endpoint = 'https://appwrite.league-tracker.com/v1';
const projectId = '61eb3...7e4ff';
const funcionsSecret = '3b4b478e5a557ab8a...c121ff21977a';
const functionId = process.argv[2];
const name = process.argv[2];
const execute = [];
const runtime = 'node-16.0';
const env_vars = {
"LT_API_ENDPOINT": endpoint,
"LT_PROJECT_ID": projectId,
"LT_FUNCTIONS_SECRET": funcionsSecret
};
// Init SDK
const client = new sdk.Client();
const functions = new sdk.Functions(client);
client
.setEndpoint(endpoint) // Your API Endpoint
.setProject(projectId) // Your project ID
.setKey('33facd6c0d792e...359362efbc35d06bfaa'); // Your secret API key
functions.get(functionId)
.then(
func => {
// Does this function already exist?
if ((typeof (func) == 'object' && func['$id'] == functionId)) {
throw `Function '${functionId}' already exists. Cannot 'create'.\n\n`;
}
// Create the function
functions.create(functionId, name, execute, runtime, env_vars)
.then(
response => console.log(response),
error => console.error(`>>> ERROR! ${error}`)
);
}).catch(
error => console.error(`>>> ERROR! ${error}`)
);
As of Appwrite 0.13.0, an Appwrite Function must expose a function that accepts a request and response. To return data, you would use the response object and either call response.json() or response.send(). The request object has an env object with all function variables. Here is an example NodeJS Function:
module.exports = async (req, res) => {
const payload =
req.payload ||
'No payload provided. Add custom data when executing function.';
const secretKey =
req.env.SECRET_KEY ||
'SECRET_KEY environment variable not found. You can set it in Function settings.';
const randomNumber = Math.random();
const trigger = req.env.APPWRITE_FUNCTION_TRIGGER;
res.json({
message: 'Hello from Appwrite!',
payload,
secretKey,
randomNumber,
trigger,
});
};
In the example above, you can see req.env.SECRET_KEY being referenced. For more information, refer to the Appwrite Functions docs.

calling store procedures within fast-csv asynchronously

I am writing a backend API in node.js and need the functionality for users to be able to upload files with data and then calling stored procedures for inserting data into MySQL. I'm thinking of using fast-csv as parser, however I am struggling with how to set up the call to stored procedure in csv stream. the idea is something like this:
var fs = require("fs");
var csv = require("fast-csv");
var stream1 = fs.createReadStream("files/testCsvFile.csv");
csv
.fromStream(stream2, { headers: true })
.on("data", function(data) {
//CALL TO SP with params from "data"//
numlines++;
})
.on("end", function() {
console.log("done");
});
In other parts of application I have set up routes as follows:
auth.post("/verified", async (req, res) => {
var user = req.session.passwordless;
if (user) {
const rawCredentials = await admin.raw(getUserRoleCredentials(user));
const { user_end, role } = await normalizeCredentials(rawCredentials);
const user_data = { user_end, role };
res.send(user_data);
} else {
res.sendStatus(401);
}
});
..that is - routes are written in async/await way with queries (all are Stored Procedures called) being defined as Promises.. I would like to follow this pattern in upload/parse csv/call SP for every line function
This is doing the job for me - - can you please describe how to achive that with your framework - - I believe it should be done somehowe, I just need to configure it correctli
//use fast-csv to stream data from a file
csv
.fromPath(form.FileName, { headers: true })
.on("data", async data => {
const query = await queryBuilder({
schema,
routine,
parameters,
request
}); //here we prepare query for calling the SP with parameters from data
winston.info(query + JSON.stringify(data));
const rawResponse = await session.raw(query); //here the query gets executed
fileRows.push(data); // push each row - for testing only
})
.on("end", function() {
console.log(fileRows);
fs.unlinkSync(form.FileName); // remove temp file
//process "fileRows" and respond
res.end(JSON.stringify(fileRows)) // - for testing
});
As mentioned in the comment, I made my scramjet to handle such a use case with ease... Please correct me if I understood it wrong, but I understand you want to call the two await lines for every CSV row in the test.
If so, your code would look like this (updated to match your comment/answer):
var fs = require("fs");
var csv = require("fast-csv");
var stream1 = fs.createReadStream("files/testCsvFile.csv");
var {DataStream} = require("scramjet");
DataStream
// the following line will convert any stream to scramjet.DataStream
.from(csv.fromStream(stream2, { headers: true }))
// the next lines controls how many simultaneous operations are made
// I assumed 16, but if you're fine with 40 or you want 1 - go for it.
.setOptions({maxParallel: 16})
// the next line will call your async function and wait until it's completed
// and control the back-pressure of the stream
.do(async (data) => {
const query = await queryBuilder({
schema,
routine,
parameters,
request
}); //here we prepare query for calling the SP with parameters from data
winston.info(query + JSON.stringify(data));
const rawResponse = await session.raw(query); //here the query gets executed
return data; // push each row - for testing only)
})
// next line will run the stream until end and return a promise
.toArray()
.then(fileRows => {
console.log(fileRows);
fs.unlinkSync(form.FileName); // remove temp file
//process "fileRows" and respond
res.end(JSON.stringify(fileRows)); // - for testing
})
.catch(e => {
res.writeHead(500); // some error handling
res.end(e.message);
})
;
// you may want to put an await statement before this, or call then to check
// for errors, which I assume is your use case.
;
To answer your comment question - if you were to use an async function in the on("data") event - you would need to create an array of promises and await Promise.all of that array on stream end - but that would need to be done synchronously - so async function in an event handler won't do it.
In scramjet this happens under the hood, so you can use the function.

Testing saves data in database

Hi I am trying to run mocha and chai test to test my node js router, which saves a user in mysql database and then returns the same array back.
The problem I am facing at the moment is that I would not like to save the information in the database when I run it from local and when I use a continious integration software like travis/Ci the test fails since there is no database connection. I would like to know how can I test the database saving with the current without actually saving to the database.
Basically meaning having a fake virtual database to save or returning save.
I read that sinon.js can help but I am quite not sure on how to use it.
Here is my code
var expect = require('chai').expect;
var faker = require('faker');
const request = require('supertest');
const should = require('should');
const sinon = require('sinon');
const helper = require('../../models/user');
describe('POST /saveUser',()=>{
it('should save a new user',(done)=>{
var fake =
request(app)
.post('/saveUser')
.send({
Owner : faker.random.number(),
firstname : faker.name.firstName(),
lastname : faker.name.lastName(),
email:faker.internet.email(),
password : faker.random.number(),
token : faker.random.uuid()
})
.expect(200)
.expect((res)=>{
expect(res.body.firstname).to.be.a("string");
expect(res.body.lastname).to.be.a("string");
expect(res.body.Owner).to.be.a("number");
})
.end(done);
});
});
This is the router
router.post('/saveUser',(req,res,next)=>{
saveUser(req.body).then((result)=>{
return res.send(req.body);
}).catch((e)=>{
return res.send('All info not saved');
});
});
And here is the model
saveUser = (userinfo) => new Promise((resolve,reject)=>{
db.query('INSERT INTO user SET ?',userinfo,function(error,results,fields){
if(error){
reject();
}else{
resolve(userinfo);
}
})
});
What you are describing is a stub. With sinon you can stub methods and call fake methods instead like this:
sinon.stub(/* module that implements saveUser */, 'saveUser').callsFake(() => Promise.resolve());