I have the following tests where I am trying to exercise my db model against a sqlite in-memory database
describe(`task model`, () => {
let Task, sandbox;
beforeEach(() => (sandbox = createSandbox()));
afterEach(() => sandbox.restore());
beforeEach(async () => {
const dbConnection = new Sequelize(`sqlite::memory:`, { logging: false });
stub(dbConnectionModule, `dbConnection`).value(dbConnection);
Task = require(`./models/task.js`).Task;
await dbConnection.sync({ force: true });
});
describe(`create a task`, () => {
let task;
beforeEach(async () => {
task = Task.build(taskData);
await task.save();
});
it(`gets an id`, () => expect(task.id).to.not.be.empty);
it(`shows up with a find all`, async () => {
const tasks = await Task.findAll();
expect(tasks).to.have.length(1);
});
});
});
I can verify the connection was newed up twice and yet my second test confusingly comes back with 2 tasks created.
It seems to be using the same db as on the first test!
I was under the impression that I would get a new in-memory db each time the Sequelize constructor is called for sqlite's in-memory db, am I misunderstanding something?
Alternately, how do I drop and recreate the database entirely for a new test?
Surprisingly, even the following after the sync doesn't seem to work
for (const model of Object.values(dbConnection.models))
model.destroy({ truncate: true });
Hihi,
What I did was to replace the database in the index file, not in the tests.
models/index.js
...
const sequelize = require('sequelize')
let sequelize;
if(process.env.NODE_ENV === 'test') {
sequelize = new Sequelize('sqlite::memory:');
}
...
and in your tests just import as usual
const { Task } = require('./models/index.js');
describe(`task model`, () => {
beforeEach(() => (sandbox = createSandbox()));
describe(`create a task`, () => {
let task;
beforeEach(async () => {
task = Task.build(taskData);
await task.save();
});
it(`gets an id`, () => expect(task.id).to.not.be.empty);
it(`shows up with a find all`, async () => {
const tasks = await Task.findAll();
expect(tasks).to.have.length(1);
});
});
});
You must/should implement a solution to init the database in every test you do.
Related
I'm trying to use jest.useFakeTimers() to make my firebase + jest tests stable when working with dates and timeouts. However, if I try to read anything from the emulated database the test times out and throws Exceeded timeout of 5000 ms for a test. Using jest.setTimeout(30000) doesn't help.
Github repro: https://github.com/vojdan/jest-fake-timers-firebase
Here's the test:
import * as admin from "firebase-admin";
// jest.setTimeout(30000);
describe("emulated firebase test", () => {
beforeAll(() => {
jest.useFakeTimers();
jest.setSystemTime(1349852318000);
admin.initializeApp({
projectId: "test-project",
databaseURL: "http://localhost:9000?ns=test-project",
});
});
afterAll(() => {
jest.useRealTimers();
});
it("only passes with fake timers", () => {
expect.assertions(1);
expect(new Date().valueOf()).toBe(1349852318000);
});
it("hangs when fake timers are used", async () => {
expect.assertions(1);
try {
const firestore = admin.firestore().doc("anything/really");
const realtime = admin.database().ref("anyhting/really");
console.log("this runs:", new Date().valueOf());
// ----- EVERYTHING RUNS UP TO HERE -----
// ----- any of the following commands will block when used with jest.useFakeTimers(); -----
// await firestore.get();
const ref = await realtime.once("value");
const value = ref.val();
console.log("this doesn't run:", value);
expect(1).toBe(1);
} catch (error) {
console.log("THERE WAS AN ERROR:", error);
}
});
});
Since useFakeTimers isn't working for me, I'm using this as a workaround:
jest.mock('date-fns', () => {
// Require the original module to not be mocked...
const originalModule = jest.requireActual('date-fns');
return {
...originalModule,
getTime: jest.fn(() => mockedTimestamp),
getUnixTime: jest.fn(() => mockedTimestampUnix),
format: jest.fn(input => 'mockedTimeString'),
};
});
I'm new to testing and I'm trying to fake a document update call using Jest. To do so I'm using react-native-testing-library and react-native-firebase.
Below is my test suite. I have one input, and when I'm changing its value the document should be updated
import firestore from "#react-native-firebase/firestore"
jest.mock("#react-native-firebase/firestore", () => {
return () => ({
collection: jest.fn(() => ({
doc: jest.fn(() => ({
update: jest.fn(),
})),
})),
});
});
describe("<EditInfosStudy>", () => {
const willSave = true;
const setDidChanged = jest.fn();
test("will update", async () => {
const { getByTestId } = render(
<EditInfosStudy willSave={willSave} setDidChanged={setDidChanged} />
);
const input = getByTestId("EditInfosStudy_TextInput");
fireEvent.changeText(input, "study text");
expect(input.props.value).toBe("study text");
// expect(firestore().collection('collection').doc('doc').update({})).toHaveBeenCalled()
// this is where i'm stuck
});
});
After Running the test Jest indicate a console.log from the function (below) to confirm that the document was updated and the test passes.
What I don't understand is how do I check if the document were updated on the last line using the expect method expect(firestore()…) I’m really confused on the whole mocking part, any help would be greatly appreciated!
belove is the function being called :
if (localChange && willSave) {
(async () => {
try {
await firestore()
.collection("users")
.doc(user.uid)
.update({ study });
setUser({ study });
console.log("study updated");
} catch (err) {
console.error(err);
}
})();
}
I would like to call an asynchronous function outside the lambda handler with by the following code:
var client;
(async () => {
var result = await initSecrets("MyWebApi");
var secret = JSON.parse(result.Payload);
client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET);
});
async function initSecrets(secretName) {
var input = {
"secretName" : secretName
};
var result = await lambda.invoke({
FunctionName: 'getSecrets',
InvocationType: "RequestResponse",
Payload: JSON.stringify(input)
}).promise();
return result;
}
exports.handler = async function (event, context) {
var myReq = await client('Request');
console.log(myReq);
};
The 'client' does not get initialized. The same code works perfectly if executed within the handler.
initSecrets contains a lambda invocation of getSecrets() which calls the AWS SecretsManager
Has anyone an idea how asynchronous functions can be properly called for initialization purpose outside the handler?
Thank you very much for your support.
I ran into a similar issue trying to get next-js to work with aws-serverless-express.
I fixed it by doing the below (using typescript so just ignore the :any type bits)
const appModule = require('./App');
let server: any = undefined;
appModule.then((expressApp: any) => {
server = createServer(expressApp, null, binaryMimeTypes);
});
function waitForServer(event: any, context: any){
setImmediate(() => {
if(!server){
waitForServer(event, context);
}else{
proxy(server, event, context);
}
});
}
exports.handler = (event: any, context: any) => {
if(server){
proxy(server, event, context);
}else{
waitForServer(event, context);
}
}
So for your code maybe something like
var client = undefined;
initSecrets("MyWebApi").then(result => {
var secret = JSON.parse(result.Payload);
client= new MyWebApiClient(secret.API_KEY, secret.API_SECRET)
})
function waitForClient(){
setImmediate(() => {
if(!client ){
waitForClient();
}else{
client('Request')
}
});
}
exports.handler = async function (event, context) {
if(client){
client('Request')
}else{
waitForClient(event, context);
}
};
client is being called before it has initialised; the client var is being "exported" (and called) before the async function would have completed. When you are calling await client() the client would still be undefined.
edit, try something like this
var client = async which => {
var result = await initSecrets("MyWebApi");
var secret = JSON.parse(result.Payload);
let api = new MyWebApiClient(secret.API_KEY, secret.API_SECRET);
return api(which) // assuming api class is returning a promise
}
async function initSecrets(secretName) {
var input = {
"secretName" : secretName
};
var result = await lambda.invoke({
FunctionName: 'getSecrets',
InvocationType: "RequestResponse",
Payload: JSON.stringify(input)
}).promise();
return result;
}
exports.handler = async function (event, context) {
var myReq = await client('Request');
console.log(myReq);
};
This can be also be solved with async/await give Node v8+
You can load your configuration in a module like so...
const fetch = require('node-fetch');
module.exports = async () => {
const config = await fetch('https://cdn.jsdelivr.net/gh/GEOLYTIX/public/z2.json');
return await config.json();
}
Then declare a _config outside the handler by require / executing the config module. Your handler must be an async function. _config will be a promise at first which you must await to resolve into the configuration object.
const _config = require('./config')();
module.exports = async (req, res) => {
const config = await _config;
res.send(config);
}
Ideally you want your initialization code to run during the initialization phase and not the invocation phase of the lambda to minimize cold start times. Synchronous code at module level runs at initialization time and AWS recently added top level await support in node14 and newer lambdas: https://aws.amazon.com/blogs/compute/using-node-js-es-modules-and-top-level-await-in-aws-lambda/ . Using this you can make the init phase wait for your async initialization code by using top level await like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
console.log("start init");
await sleep(1000);
console.log("end init");
export const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
This works great if you are using ES modules. If for some reason you are stuck using commonjs (e.g. because your tooling like jest or ts-node doesn't yet fully support ES modules) then you can make your commonjs module look like an es module by making it export a Promise that waits on your initialization rather than exporting an object. Like so:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const main = async () => {
console.log("start init");
await sleep(1000);
console.log("end init");
const handler = async (event) => {
return {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
};
return { handler };
};
# note we aren't exporting main here, but rather the result
# of calling main() which is a promise resolving to {handler}:
module.exports = main();
I'm trying to convert my VueJS app to NuxtJS to work with SSR. I'm stuck trying to get data loaded with asyncData. When I add my query to a 'mounted() {}' function it works fine but I can't get it to work with asyncData(){} so that I can use SSR.
Does anyone have any idea how to fix this.
My code:
<ul>
<li v-for='province in provinces' v-bind:key="province.id"> {{province.name_nl}}</li>
</ul>
asyncData () {
return { msg: 'Welcome to my new app' }
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
return firebase.firestore()
.collection('provinces')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
return {provinces: provincesArray}
});
},
Or is there another way I should be doing this? Keeping in mind that it does have to work with SSR.
PS: Yes this code is inside my pages folder, not the component, I know that's not allowed.
When using asyncDate() you can add the async and await keywords to wait for the response(s) before returning them.
How I solved it:
async asyncData () {
const moment = require("moment");
var date = moment(new Date()).format("YYYY-MM-DD");
let housesArray = []
let provincesArray = []
await firebase.firestore()
.collection('provinces')
.orderBy('name_nl')
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
provincesArray.push(doc.data());
});
});
await firebase.firestore()
.collection("houses")
.where("valid_until", ">", date)
.get()
.then(querySnapshot => {
querySnapshot.forEach(doc => {
housesArray.push(doc.data());
});
});
return {
provinces: provincesArray,
houses: housesArray
}
},
I have following sample function from this tutorial: Asynchronous Programming (I Promise!) with Cloud Functions for Firebase - Firecasts
exports.emailEmployeeReport = functions.database
.ref('/employees/${eid}/reports/${rid}')
.onWrite(event => {
const eid = event.params.eid;
const report = event.data.val().report;
const root = event.data.ref.root;
const mgr_promise = root.child(`/employees/${eid}/manager`).once('value');
const then_promise = mgr_promise.then(snap => {
const mgr_id = snap.val();
const email_promise = root.child(`/employees/${mgr_id}/email`).once('value');
return email_promise;
}).catch(reason => {
// Handle the error
console.log(reason);
});;
const then_promise2 = then_promise.then(snap => {
const email = snap.val();
const emailReportPromise = sendReportEmail(email, report);
return emailReportPromise;
}).catch(reason => {
// Handle the error
console.log(reason);
});
return then_promise2;
});
var sendReportEmail = function (email, report) {
const myFirstPromise = new Promise((resolve, reject) => {
// do something asynchronous which eventually calls either:
//
setTimeout(function () {
try {
var someValue = "sendReportEmail";
console.log(someValue);
// fulfilled
resolve(someValue);
}
catch (ex) {
// rejected
reject(ex);
}
}, 2000);
});
return myFirstPromise;
}
once I run firebase deploy command, eventually I am getting following error:
functions[emailEmployeeReport]: Deploy Error: Failed to configure
trigger
providers/google.firebase.database/eventTypes/ref.write#firebaseio.com
(emailEmployeeReport)
I also have a simple hello-world method and a similar trigger method, and they deploy fine.
Am I missing something here?
The syntax for wildcards in the database reference does not have "$".
Try the following:
exports.emailEmployeeReport = functions.database
.ref('/employees/{eid}/reports/{rid}')