Mock ArangoJS on NodeJS unit test - node.js

I am using ArangoDB as storage of my sample JSON license file. I have a function that contains an AQL query like this:
listLicenses: async function listLicenses() {
let result = [];
await this.useLicenseDb();
const licenseCol = await db.collection('licenseCollection');
try {
const query = {
query: "FOR doc in licenseCollection RETURN {licenseId: doc.licenseId, licenseName: doc.licenseName, clientName: doc.clientName, useCase: doc.useCase, expirationDate: doc.expirationDate, callType: doc.callType}"
};
const cursor = await db.query(query);
result = await cursor.all();
} catch (err) {
console.error(err);
return err;
}
return result;
}
This function is querying any document present in licenseCollection collection inside an ArangoDB.
What I need is to create a unit test that calls/mocks this function, without using/needing ArangoDB Client/Server. Anyone has done like this or similar to this?
Thank you!

Related

Function that return undefined in node.js. Inside the function I have mongoDB.connect

I tried to make function for my project in the service. This service need to check is user exists in my database, but my function(this function is checking) inside the class return undefined.
This is my code:
const express = require("express");
const mongoDB = require('mongodb').MongoClient;
const url = "here I paste url to my databse(everything is good here)";
class CheckService {
isUserExists(username) {
mongoDB.connect(url, (error, connection) => {
if (error) {
console.log("Error", '\n', error);
throw error;
}
const query = {name: username};
const db = connection.db("users");
const result = db.collection("users").find(query).toArray(
function findUser(error, result) {
if (error) {
throw error;
}
const arr = [];
if (result.value === arr.value) {
console.log(false);
connection.close();
return false;
} else {
console.log(true);
console.log(arr);
console.log(result);
connection.close();
return true;
}
});
console.log(result);
});
}
}
module.exports = new CheckService();
I imported my service to another service:
const checkService = require('./check.service');
After this, I invoked my function from my service like this:
console.log('function:',checkService.isUserExists(username));
I expected good result, but function doesn't return, that I want, unfortunately.
Please help!
There are two problems with your function
it doesn't return anything
toArray() is a promise, so your console.log probably just prints a promise.
Probably you're looking for something like this:
class CheckService {
async isUserExists(username) {
const connection = await mongoDB.connect(url);
const query = {name: username};
const db = connection.db("users");
const result = await db.collection("users").find(query).toArray();
connection.close();
return result.length !== 0;
}
}
You'd then invoke it with something like
await new CheckService().isUserExists(username);
Though, it's worth noting that you probably don't need toArray and instead could use findOne or even count() since all you care about is existence. I probably also wouldn't instantiate a new connection each time (but that's not super relevant)
2 things wrong here. Firstly the first comment is correct. You're only logging the result and not passing back to the caller. Secondly, a quick peek at the mongo docs shows that retrieval methods are promises. You need to make the function async and add awaits where needed.
Mongo:
https://www.mongodb.com/docs/drivers/node/current/fundamentals/crud/read-operations/retrieve/
Promises:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

Synchronously iterate through firestore collection

I have a firebase callable function that does some batch processing on documents in a collection.
The steps are
Copy document to a separate collection, archive it
Run http request to third party service based on data in document
If 2 was successful, delete document
I'm having trouble with forcing the code to run synchronously. I can't figure out the correct await syntax.
async function archiveOrders (myCollection: string) {
//get documents in array for iterating
const currentOrders = [];
console.log('getting current orders');
await db.collection(myCollection).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
currentOrders.push(doc.data());
});
});
console.log(currentOrders);
//copy Orders
currentOrders.forEach (async (doc) => {
if (something about doc data is true ) {
let id = "";
id = doc.id.toString();
await db.collection(myCollection).doc(id).set(doc);
console.log('this was copied: ' + id, doc);
}
});
}
To solve the problem I made a separate function call which returns a promise that I can await for.
I also leveraged the QuerySnapshot which returns an array of all the documents in this QuerySnapshot. See here for usage.
// from inside cloud function
// using firebase node.js admin sdk
const current_orders = await db.collection("currentOrders").get();
for (let index = 0; index < current_orders.docs.length; index++) {
const order = current_orders.docs[index];
await archive(order);
}
async function archive(doc) {
let docData = await doc.data();
if (conditional logic....) {
try {
// await make third party api request
await db.collection("currentOrders").doc(id).delete();
}
catch (err) {
console.log(err)
}
} //end if
} //end archive
Now i'm not familiar with firebase so you will have to tell me if there is something wrong with how i access the data.
You can use await Promise.all() to wait for all promises to resolve before you continue the execution of the function, Promise.all() will fire all requests simultaneously and will not wait for one to finish before firing the next one.
Also although the syntax of async/await looks synchronous, things still happen asynchronously
async function archiveOrders(myCollection: string) {
console.log('getting current orders')
const querySnapshot = await db.collection(myCollection).get()
const currentOrders = querySnapshot.docs.map(doc => doc.data())
console.log(currentOrders)
await Promise.all(currentOrders.map((doc) => {
if (something something) {
return db.collection(myCollection).doc(doc.id.toString()).set(doc)
}
}))
console.log('copied orders')
}

`find()` Not Working Like `findOne()` After Connecting to MongoDB Via Node

I am familiar with how to get documents from the Mongo shell, but am having difficulty getting documents using find() when connecting via Node.
What I'm getting right now looks like a lot of cursor info, but not the actual documents.
What do I need to change with the following code so that I get the actual documents logged to the console for 'results'?
const config = require('./../../../configuration');
const url = config.get('MONGO_URL');
const dbName = config.get('MONGO_DATABASE');
const MongoClient = require('mongodb').MongoClient;
const client = new MongoClient(url);
module.exports = async function getSchedules() {
let results;
return new Promise((resolve, reject) => {
client.connect(async function (err) {
if (err) return reject(err);
try {
const db = await client.db(dbName);
results = await db.collection('schedules').find();
} catch (error) {
return reject(error);
}
return resolve(results);
});
});
};
... And here's where I actually try and get the documents:
async function getSchedulesFromDB() {
await getSchedules().then((schedules => {
console.log('schedules: ', schedules); // expect result here
return schedules;
}));
}
When I used this same kind of code structure on a findOne(), it worked. But here when using a find() it is not. What am I missing? Does find() work fundamentally differently than findOne()?
Yes. find() returns a cursor over which you must iterate. findOne() returns a single doc, not a cursor. If you want an array of results, you must "build it yourself" by iterating the cursor, something like:
results = [];
db.collection('schedules').find().forEach(function(d) { results.push(d); });

nodejs mongodb - how to return the inserted document using insertOne

I am using "mongodb": "^3.1.6",.
I have a method using the drivers insertOne method (shops is my mongoDb database collection):
/**
* Adds a new shop to the shops collection
* #param {Shop} doc - the new shop to add
*/
static async addShop(shop) {
try {
return await shops.insertOne(shop, {}, (err, result) => {
if (err) {
throw e
}
return result
})
// TODO this should return the new shop
} catch (e) {
console.error(`Something went wrong in addShop : ${e}`)
throw e
}
}
Now the method inserts the document into the collection as expected, but does not return the insert result. How do I return the result value of the callback function?
For reference - I wrote this unit test that I want to get to pass:
test("addShop returns the added shop", async () => {
const testShop = {
name: "Test shop for jest unit tests",
}
const newShop = await ShopsDAO.addShop(testShop)
const shoppingCart = global.DBClient.db(process.env.NS)
const collection = shoppingCart.collection("shops")
expect(newShop.name).toEqual(testShop.name)
await collection.remove({ name: testShop.name })
})
Thanks for the help.
I suggest you not to mix promises and callbacks as it is a bad way to organize your code. According to docs, if you do not pass callback insertOne will return Promise. So I suggest you to rewrite function smth like that:
static async addShop(shop) {
try {
return shops.insertOne(shop)
} catch (insertError) {
console.error(`Something went wrong in addShop : ${insertError}`)
throw insertError
}
}
This addShop will return promise, use await to retrieve data from it:
const newShop = await ShopsDAO.addShop(testShop)
P.S. You can also omit await with return statement

this.parent.acquire is not a function on prepared statements

I use the mssql (https://www.npmjs.com/package/mssql) module for my database. Normally I use postgres databases which lead to pg (https://www.npmjs.com/package/pg).
I want to setup prepared statements for the mssql database. When using the pg module it's quite easy.
This is how I do it with pg:
I setup my databaseManager
const { Pool } = require('pg');
const db = require('../config/database.js');
const pool = new Pool(db);
function queryResponse(result, err) {
return {
result,
err
};
}
module.exports = async (text, values) => {
try {
const result = await pool.query(text, values);
return queryResponse(result.rows, null);
} catch (err) {
return queryResponse(null, err);
}
};
and whenever I want to query the database I can call this module and pass in my statement and values. An example for todo apps would be
todos.js (query file)
const db = require('../databaseManager.js');
module.exports = {
getAllTodosFromUser: values => {
const text = `
SELECT
id,
name,
is_completed AS "isCompleted"
FROM
todo
WHERE
owner_id = $1;
`;
return db(text, values);
}
};
I wanted to create an mssql equivalent. From the docs I see that the module differs from the pg module.
I changed my databaseManager to
const sql = require('mssql');
const config = require('../config/database.js');
const pool = new sql.ConnectionPool(config).connect();
module.exports = async (queryBuilder) => {
try {
const preparedStatement = await new sql.PreparedStatement(pool);
return queryBuilder(sql, preparedStatement, async (query, values) => {
await preparedStatement.prepare(query);
const result = await preparedStatement.execute(values);
await preparedStatement.unprepare();
return {
result: result.rows,
err: null
};
});
} catch (err) {
return {
result: null,
err
}
}
};
and my query file would pass in the required parameters for the preparedStatement object
const db = require('../databaseManager.js');
module.exports = {
getUserByName: username => db((dataTypes, statementConfigurator, processor) => {
statementConfigurator.input('username', dataTypes.VarChar);
const query = `
SELECT
*
FROM
person
WHERE
username = #username;
`;
return processor(query, { username });
})
};
I was hoping that this approach would return the desired result but I get the error
this.parent.acquire is not a function
and don't know if my code is wrong. If it is, how can I setup my prepared statements correctly?
Edit:
I just found out that the error comes from this line of code
await preparedStatement.prepare(query);
but I think I took it correctly from the docs
https://tediousjs.github.io/node-mssql/#prepared-statement
I thought this question deserved a little bit more explanation than the answer what OP gave. The solution is no different than what OP already answered.
The issue remains same, pool mustn't have resolved from its promise pending state. So it just has to be awaited.
module.exports = async queryBuilder => {
try {
await pool; // Waiting for pool resolve from promise pending state.
const preparedStatement = await new sql.PreparedStatement(pool);
// ..
} catch (err) {
// ..
}
};
When you try to build a prepared statement, you pass the pool as an argument to its constructor. In its constructor is the below line
this.parent = parent || globalConnection
After which when you prepare the statement the flow leads to this line which would cause the issue since at that time this.parent's value was still a promise which was yet to be resolved.
this.parent.acquire(this, (err, connection, config) => {

Resources