I want to mock a SQL Server connection.
Here is the code to be tested:
import sql from 'mssql';
export class DBConnection {
public async query(input: any): Promise<any> {
const config = {
server: "server",
port: 1234,
database: "db",
user: "user",
password: "password",
trustServerCertificate: true
};
try {
const pool = new sql.ConnectionPool(config);
await pool.connect();
let request = pool.request();
request.input('key', sql.VarChar, input.key);
request.input('value', sql.VarChar, input.value);
let insertQuery = "INSERT INTO SOMETABLE (key, value) OUTPUT INSERTED.key VALUES (#key, #value)"
return await request.query(insertQuery);
} catch (err) {
console.error(err);
}
}
}
My question is: how to create mock of sql.ConnectionPool? I tried using jest.Mocked to mock entire module, tried using jest.spy to mock the constructor or specific method, but seems none of them working
Here is the mock test, apparently not working
import { DBConnection } from './db';
import sql from 'mssql';
jest.mock('mssql');
const mockpool = sql as jest.Mocked<typeof sql>;
const pool = mockpool.ConnectionPool;
let db = new DBConnection();
describe('DB test', () => {
beforeEach(() => {
jest.spyOn(sql.ConnectionPool.prototype, 'connect').mockImplementation(() => pool);
});
afterEach(() => {
jest.resetAllMocks();
});
it('should have success response', async () => {
const input = { key: 'key1', value: 1234 }
try {
const response = await db.query(input);
expect(response).toBe("key1");
} catch (e) {
}
});
});
Related
I have been following a tutorial on how to create my first Web3js application with solidity and react.
The tutorial was going great until I fall into this problem with metamask RPC.
The tutorial I have been following is this: https://www.youtube.com/watch?v=Wn_Kb3MR_cU&t=6333s&ab_channel=JavaScriptMastery
Right now I'm getting the following errors when trying to run function from ethereum:
inpage.js:1 MetaMask - RPC Error: The method "accounts " does not exist / is not available.
inpage.js:1 MetaMask - RPC Error: The method "eth_accounts " does not exist / is not available.
uncaught (in promise) {code: -32601, message: 'The method "eth_accounts " does not exist / is not available.', data: {…}, stack: '{\n "code": -32601,\n "message": "The method \\"eth…beogaeaoehlefnkodbefgpgknn/common-0.js:18:167275)'}
uncaught (in promise) {code: -32601, message: 'The method "eth_requestAccounts " does not exist / is not available.', data: {…}, stack: '{\n "code": -32601,\n "message": "The method \\"eth…beogaeaoehlefnkodbefgpgknn/common-0.js:18:167275)'}
The file that runs this is a context file TransactionContext.tsx:
import React, { useEffect, useState } from 'react';
import { ethers } from 'ethers';
import { contractABI, contractAddress } from '../utils/constants';
export const TransactionContext = React.createContext({} as any);
const { ethereum } = window as any;
const getEthereumContract = () => {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const transactionsContract = new ethers.Contract(contractAddress, contractABI, signer);
console.log({
provider,
signer,
transactionsContract
})
}
export const TransactionProvider = ({ children }: any) => {
const [currentAccount, setCurrentAccount] = useState('');
const checkIfWalletIsConnected = async () => {
if (!ethereum) return alert("Please install metamask!");
const accounts = await ethereum.request({ method: 'eth_accounts '});
console.log(accounts);
}
const connectWallet = async () => {
try {
if (!ethereum) return alert("Please install metamask!");
const accounts = await ethereum.request({ method: 'eth_requestAccounts '});
setCurrentAccount(accounts[0]);
} catch (e) {
console.log(e);
throw new Error('No Ethereum object.')
}
}
useEffect(() => {
checkIfWalletIsConnected();
}, [])
return (
<TransactionContext.Provider value={{ connectWallet }}>
{children}
</TransactionContext.Provider>
)
}
I see 3 issues in your contract:
1- you are not returning the contract from getEthereumContract. it should be
const getEthereumContract = () => {
const provider = new ethers.providers.Web3Provider(ethereum);
const signer = provider.getSigner();
const transactionsContract = new ethers.Contract(contractAddress, contractABI, signer);
return transactionsContract
}
I dont see you are using here yet but you might get bug in the future:
2- Error says 'The method "eth_accounts " does not exist ... you have extra space here "eth_accounts ". should be
const accounts = await ethereum.request({ method: 'eth_accounts'});
3- this is similar to second. You have extra space
const accounts = await ethereum.request({ method: 'eth_requestAccounts'});
You need to specify for which account you want to get the signer provider.getSigner(account)
On click of your connect button add this
async function connectWalletHandler() {
if (!ethereum) {
console.log("Make sure you have Metamask installed");
return;
} else {
console.log("Wallet exist");
}
const accounts = await ethereum.request({ method: "eth_requestAccounts" });
if (accounts.length !== 0) {
} else {
console.log("No authorized account found");
}
And put this code in you app.js
const [user,setUser]=useState(null);
Useeffect(()=>{
if (window.ethereum) {
const isMetaMaskConnected = async () => {
let provider = new ethers.providers.Web3Provider(window.ethereum);
const accounts = await provider.listAccounts();
let account = null;
if (accounts.length > 0) {
account = accounts[0];
}
let signer = provider.getSigner(account);
setUser({ provider: provider, signer: signer, account: account });
};
isMetaMaskConnected();
window.ethereum.on("chainChanged", (chainId) => {
window.location.reload();
});
window.ethereum.on("accountsChanged", () => {
window.location.reload();
});
} else {
}},[])
From now you have 3 option first user is null metamask not installed
2 user.account="" or null metamask installed and connected but locket
3 user.account have value this is when the wallet connected to the website and every thing 👍
export async function insertAttempt() {
const db = await openDatabase()
try {
return await new Promise((resolve, reject) => {
db.transaction(
(tx) => {
tx.executeSql("INSERT INTO Attempt (attempt_date)
VALUES (?)", [Date.now()])
tx.executeSql(query.selectAttempt, [], (transaction, resultSet) => {
console.log(resultSet)
})
},
reject,
resolve
)
})
} catch (e) {
console.log("Error: ", e)
}
}
I'm calling the above in a react hook component, like so:
useEffect(() => {
async function example() {
await insertAttempt()
}
example()
}, [])
Error: [Error: attempt to write a readonly database (code 1032 SQLITE_READONLY_DBMOVED)]
I don't have this problem with select sql operations, only with inserts.
If you are using a Pre-populated database in your App, go to your openDatabase() function, you may need to access the private attribute _db of WebSQLDatabase and close it with available method, you will end up with something similar to below.
export default async function openDatabase() {
const database = SQLite.openDatabase("myDb.db")
database._db.close()
if (!(await FileSystem.getInfoAsync(FileSystem.documentDirectory + "SQLite")).exists) {
await FileSystem.makeDirectoryAsync(FileSystem.documentDirectory + "SQLite");
}
await FileSystem.downloadAsync(
Asset.fromModule(require("../assets/www/myDb.db")).uri,
FileSystem.documentDirectory + "SQLite/myDb.db"
);
return SQLite.openDatabase("myDb.db");
}
I am trying to send the validation email upon the account registration, using firebase. The registration is being done successfully but whenever I try to code email verification it gives me an error. Probably because I don't know where to place it. All my firebase methods are on Fire.js, which are the following:
import firebaseKeys from './Config';
import firebase from 'firebase';
require("firebase/firestore");
class Fire {
constructor() {
if (!firebase.apps.length) {
firebase.initializeApp(firebaseKeys);
}
}
addPost = async ({ text, localUri }) => {
const remoteUri = await this.uploadPhotoAsync(localUri, 'photos/${this.uid}/${Date.now()}');
return new Promise((res, rej) => {
this.firestore.collection('posts').add({
text,
uid: this.uid,
timestamp: this.timestamp,
image: remoteUri
})
.then(ref => {
res(ref);
})
.catch(error => {
rej(error);
});
});
}
uploadPhotoAsync = async (uri, filename) => {
return new Promise(async (res, rej) => {
const response = await fetch(uri);
const file = await response.blob();
let upload = firebase
.storage()
.ref(filename)
.put(file);
upload.on(
"state_changed",
snapshot => {},
err => {
rej(err);
},
async () => {
const url = await upload.snapshot.ref.getDownloadURL();
res(url);
}
);
});
}
createUser = async user => {
let remoteUri = null
try {
await firebase.auth().createUserWithEmailAndPassword(user.email, user.password)
//I tried to code it here with user.sendEmailVerification();
let db = this.firestore.collection("users").doc(this.uid)
db.set({
name: user.name,
email: user.email,
avatar: null
})
if (user.avatar) {
remoteUri = await this.uploadPhotoAsync(user.avatar, 'avatars/${this.uid}')
db.set({avatar: remoteUri}, {merge: true});
}
} catch (error) {
alert("Error: ", error);
}
};
get firestore() {
return firebase.firestore();
}
get uid() {
return (firebase.auth().currentUser || {}).uid;
}
get timestamp() {
return Date.now();
}
}
Fire.shared = new Fire();
export default Fire;
The createUserWithEmailAndPassword() method returns a Promise which resolves with a UserCredential AND (as the the doc indicates) "on successful creation of the user account, this user will also be signed in to your application."
So you can easily get the signed in user by using the user property of the UserCredential, and call the sendEmailVerification() method, as follows:
try {
const userCredential = await firebase.auth().createUserWithEmailAndPassword(user.email, user.password);
await userCredential.user.sendEmailVerification();
//In the next line, you should most probably use userCredential.user.uid as the ID of the Firestore document (instead of this.uid)
cont db = this.firestore.collection("users").doc(this.uid);
//...
} catch (...)
Note that you may pass an ActionCodeSettings object to the sendEmailVerification() method, see the doc.
I am using this code to connect to my SQL Server and retrieve some data which works fine, if I only call the code once. If I call it twice I get this error:
ConnectionError: Already connecting to database! Call close before connecting to different database.at ConnectionPool._connect
But I am closing the conn after the call so I'm not sure what I am missing.
var sql = require('mssql');
const pool = new sql.ConnectionPool({
user: 'sa',
password: 'password',
server: '192.168.1.2',
database: 'demo',
options: {
encrypt: false
}
})
var conn = pool;
module.exports.getCounter = function( query){
conn.connect().then(function (err) {
var req = new sql.Request(conn);
req.query(query).then(function (result) {
console.log(result.recordset);
return result.recordset;
conn.close();
})
.catch(function (err) {
console.log(err);
conn.close();
});
})
.catch(function (err) {
console.log(err);
})};
You're returning the value before closing the connection, hence the function terminates before reaching that line. So just move the return statement below your conn.close(). The other issues you might have afterwards is that you might be calling your function twice before one executes and terminates completely, since those calls are asynchronous.
You might have to set your getCounter function as a Promise, so that you can wait for its completion/failure before calling it again. Off the top of my head in your example:
const getCounter = () => new Promise((resolve,reject) => {
conn.connect().then(function (err) {
var req = new sql.Request(conn);
req.query(query).then(function (result) {
conn.close();
resolve(result);
})
.catch(function (err) {
conn.close();
reject(err);
});
})
})
You can call your function afterwards as getCounter().then((result) => {...})
Here is another way to solve it which might be helpful for others.
const sql = require('mssql')
let connectionPoolConfig = {
user: 'sa',
password: 'password',
server: '192.168.1.2',
database: 'demo',
options: {
encrypt: false
}
}
let connectionPoolPromise = null
let connectionPoolObj = null
let getOrCreatePool = async () => {
if (connectionPoolObj) {
return connectionPoolObj
} else if (!connectionPoolPromise) {
connectionPoolPromise = new sql.ConnectionPool(connectionPoolConfig).connect()
}
connectionPoolObj = await connectionPoolPromise
return connectionPoolObj
}
let query = async(sql) => {
const pool = await getOrCreatePool()
return await pool.request().query(sql)
}
module.exports = {
query: query
}
And here is how to call it
let testCallerSQL = async () => {
try {
const res = await sqlUtil.query('select * from mytable')
console.log(res.recordset)
} catch(err) {
console.log(err)
} finally {
}
}
I'm writing a function which makes an insert using mssql every time it is called. The function received 2 params (action, user), which is the data inserted into the SQL Server table.
Function looks like this:
function saveActionToDB(action, user) {
if (config.Logging.DB.type == 'mssql') {
const dbOptions = {
user: config.Logging.DB.user,
password: config.Logging.DB.password,
server: config.Logging.DB.server,
database: config.Logging.DB.database,
options: {
encrypt: config.Logging.DB.encrypt
}
};
const database = require('mssql');
const emmitQuery = async () => {
try {
const pool = await database.connect(dbOptions);
const request = pool.request();
request.input('action', sql.VarChar, action);
request.input('user', sql.VarChar, user);
request.query('insert into actions (action, user) values (#action, #user)', (err, result)=>{
//if (err) combinedLogger.error(err);
});
}
catch (err) {
combinedLogger.error(err);
}
}
emmitQuery();
}
else if(config.Logging.DB.type == 'oracle') {
//oracle
}
}
However, I am getting a console error with "undefined", which means that the try is catching an error, however, the err it's undefined, so I can't figure out what the error is.
Can anyone help?