Trying to test firebase rules with emulator - javascript

I'm trying to setup my testEnvironment for testing my firestore rules with my locally running emulator.
When i try to access testEnv it is undefined, and I guess that has something to do with initializeTestEnvironment being asynchronous, but how do I get around that?
import {
assertFails,
assertSucceeds,
initializeTestEnvironment,
RulesTestEnvironment,
} from "#firebase/rules-unit-testing"
import fs from 'fs'
const projectId = 'comment-section-e9c09';
let testEnv:RulesTestEnvironment;
beforeAll( async () => {
let testEnv = await initializeTestEnvironment({
projectId: projectId,
firestore: {
rules: fs.readFileSync("firestore.rules", "utf8"),
host:'localhost',
port:8080
},
});
})
test("should clear db",() => {
testEnv.clearDatabase()
})

Related

How to use setDoc with Firebase-Admin with Typescript in firestore?

I have config/firebase.ts:
import { initializeApp, cert } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore'
const firebaseAdminApp = initializeApp({
credential: cert({
privateKey: process.env.NEXT_PUBLIC_FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
clientEmail: process.env.NEXT_PUBLIC_FIREBASE_SERVICE_EMAIL,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID
}),
databaseURL: `https://${process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID}.firebaseio.com`
});
const firestore = getFirestore(firebaseAdminApp);
export default firestore
and when trying to upsert, I have:
import firestore from "../config/firebaseAdmin";
const upsertInstance = async (instance: Instance) => {
const hashedUri = createHash('sha256').update(instance.uri).digest('hex')
const res = await firestore.doc(`instances/${hashedUri}`).set(instance);
return res
}
but I get:
Error: expected a function
What am I doing wrong?
Firebase Admin is not totally modular yet like the client SDK yet so you would have to use namespaced syntax. Admin SDK's Firestore instance won't work perfectly with client SDK functions. Try refactoring the code as shown below:
export const db = getFirestore(firebaseAdminApp);
import { db } from "../path/to/firebase"
const upsertInstance = async (instance: Instance) => {
const res = await db.doc(`instances/${instance.uri}`).set(instance);
return res;
}
Checkout the documentation for more information.

Pub Sub Cloud Function - Async Await

Trying to accomplish the following via a scheduled Firebase Function:
Fetch Firestore Collection
Delete Collection
Make Get Request to External API
Write Get Request results to Firestore Collection.
The function is failing to deploy. Error logs only say {"code":3,"message":"Function failed on loading user code. This is likely due to a bug in the user code.
When I run the emulator suite locally, I get no errors, but the DB does not update.
I'm getting a linting error on the "=>" that's attached to the "onRun" method. Not sure if this is a code problem or an ESLint problem for ES6 in Firebase Functions.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const axios = require('axios');
admin.initializeApp();
const db = admin.firestore();
exports.scheduledFunction = functions.pubsub
.schedule("every 2 minutes")
.timeZone("America/New_York")
.onRun(async (context) => {
try {
const querySnapshot = await db.collection("my_collection").get();
console.log(querySnapshot.docs)
const promises = querySnapshot.docs.map(doc => db.collection("my_collection").doc(doc.id).delete());
await Promise.all(promises);
const options = {
"method": "get",
"url": "www.myUrl.com",
"headers": {
"Cookie": "...",
},
};
const axiosResponse = await axios(options);
const apiResponse = JSON.parse(axiosResponse.data);
const parsedResponse = apiResponse["news_results"];
const docCreationPromises = parsedResponse.map(response => db.collection("my_collection").add(parsedResponse))
await Promise.all(docCreationPromises);
return null;
} catch (error) {
console.log(error);
return null;
}
});
For the ESLint issue with =>, try setting the ecmaVersion to 8 in your ESLint config.
module.exports = {
root: true,
env: {
es6: true,
node: true,
},
extends: [
"eslint:recommended",
"google",
],
parserOptions: {
ecmaVersion: 8
}
};
When I run the emulator suite locally, I get no errors, but the DB does not update.
Are you using Firestore emulator? If yes, then data will be added there and not in production so you can see the data in emulator only.

Typeorm Connection "default" was not found when connection is created in jest globalSetup

I'm having a similar problem as in #5164 and this question. Consider the following working test code:
// AccountResolver.test.ts
describe('Account entity', () => {
it('add account', async () => {
await createConnections()
const defaultConnection = getConnection('default')
const actual = await callGraphql(
`mutation {
addAccount(options: {
accountIdentifier: "7csdcd8-8a5f-49c3-ab9a-0198d42dd253"
name: "Jake, Bob (Braine-l’Alleud) JAM"
userName: "Bob.Marley#contoso.com"
}) {
accountIdentifier
name
userName
}
}`
)
expect(actual.data).toMatchObject({
data: {
addAccount: {
accountIdentifier: '7csdcd8-8a5f-49c3-ab9a-0198d42dd253',
name: 'Jake, Bob (Braine-l’Alleud) JAM',
userName: 'Bob.Marley#contoso.com',
},
},
})
await defaultConnection.query(`DELETE FROM Account`)
await defaultConnection.close()
})
})
The code to create a connection and close it should be executed before all tests and after all tests are done, that's why we've added it to globalSetup.ts and globalTeardown.ts:
// globalSetup.ts
require('ts-node/register')
import { createConnections } from 'typeorm'
module.exports = async () => {
// console.log('jest setup')
await createConnections()
}
// globalTeardown.ts
require('ts-node/register')
import { getConnection } from 'typeorm'
module.exports = async () => {
const defaultConnection = getConnection('default')
await defaultConnection.close()
}
// AccountResolver.test.ts
describe('Account entity', () => {
it('add account', async () => {
const defaultConnection = getConnection('default')
await defaultConnection.query(`DELETE FROM Account`)
const actual = await callGraphql(
`mutation {
addAccount(options: {
accountIdentifier: "7csdcd8-8a5f-49c3-ab9a-0198d42dd253"
name: "Jake, Bob (Braine-l’Alleud) JAM"
userName: "Bob.Marley#contoso.com"
}) {
accountIdentifier
name
userName
}
}`
)
expect(actual.data).toMatchObject({
data: {
addAccount: {
accountIdentifier: '7csdcd8-8a5f-49c3-ab9a-0198d42dd253',
name: 'Jake, Bob (Braine-l’Alleud) JAM',
userName: 'Bob.Marley#contoso.com',
},
},
})
})
})
Omitting the line require('ts-node/register') from both files throws this error:
T:\Test\src\it-portal\entity\Account.ts:1
import {
^^^^^^
SyntaxError: Cannot use import statement outside a module
Keeping the require line in throws:
FAIL src/resolvers/AccountResolver.test.ts × add account (31 ms) ●
Account entity › add account ConnectionNotFoundError: Connection
"default" was not found.Account entity
Version
"jest": "^26.0.1",
"ts-jest": "^26.1.0",
"ts-node-dev": "^1.0.0-pre.44",
"typescript": "^3.9.5"
Config
// jest.config.js
module.exports = {
preset: 'ts-jest',
globalSetup: './src/test-utils/config/globalSetup.ts',
globalTeardown: './src/test-utils/config/globalTeardown.ts',
setupFiles: ['./src/test-utils/config/setupFiles.ts'],
moduleDirectories: ['node_modules', 'src'],
globals: {
'ts-jest': {
tsConfig: 'tsconfig.json',
diagnostics: {
warnOnly: true,
},
},
},
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
coverageReporters: ['json', 'lcov', 'text', 'clover'],
}
Thank you for pointing out my mistakes. As I'm new I tried googling but couldn't really find an answer if this is me not understanding the tool or a bug in the too. Found a similar issue here with a PR.
It seems like the tests are running in a fully isolated environment where they can't access the connection set up within globalSetup.
Workaround
The only workaround I have found thus far is to add the following code to every test file:
beforeAll(async () => {
await createConnections()
})
afterAll(async () => {
const defaultConnection = getConnection('default')
await defaultConnection.close()
})
require('ts-node/register') shouldn't present in .ts files. They are already processed by TypeScript compiler.
This is not what globalSetup and globalTeardown are for. They run in Jest parent process and are evaluated once, while each test suite runs in child processes.
This can be achieved by providing a common setup in setupFilesAfterEnv option:
// jest.setup.ts
...
beforeAll(async () => {
await createConnections()
})
afterAll(async () => {
const defaultConnection = getConnection('default')
await defaultConnection.close()
})
Since Jest tests run in parallel, this will result in multiple database connections. If this is not desirable because of connection limit, Jest runInBand option needs to be used.
A setup for all tests not desirable because not all test suites need database connection, while they will unconditionally take time and occupy database connection pool. In this case jest.setup.ts can be imported directly in tests that use a database instead of setupFilesAfterEnv, no need to specify beforeAll and afterAll in each suite.

TypeError in "path" when running Firebase Functions Test

I'm writing a test for a google cloud function which'll write some information to a firestore database. The test uses firebase-functions-test and jest. The function I'm writing works successfully when I deploy it but when I try to run the test I get:
TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type object
at GrpcClient.loadProto (node_modules/google-gax/src/grpc.ts:182:23)
at new FirestoreClient (node_modules/#google-cloud/firestore/build/src/v1/firestore_client.js:113:32)
at ClientPool.Firestore._clientPool.pool_1.ClientPool [as clientFactory] (node_modules/#google-cloud/firestore/build/src/index.js:319:26)
at ClientPool.acquire (node_modules/#google-cloud/firestore/build/src/pool.js:81:35)
at ClientPool.run (node_modules/#google-cloud/firestore/build/src/pool.js:155:29)
at Firestore.request (node_modules/#google-cloud/firestore/build/src/index.js:885:33)
at WriteBatch.commit_ (node_modules/#google-cloud/firestore/build/src/write-batch.js:450:14)
My function:
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const db = admin.firestore();
const saveCheckup = functions.pubsub.topic('save-test').onPublish((message) => {
const {url, ...dataToSave} = message.attributes;
let current = db.collection('current').doc(url);
current.set(dataToSave, {merge: true})
return true;
});
module.exports = saveCheckup;
My test:
import * as admin from 'firebase-admin';
const testEnv = require('firebase-functions-test')(
{
databaseURL: "https://my-project.firebaseio.com",
projectId: 'my-project',
storageBucket: 'my-project.appspot.com'
}, "./my-project-firebase-adminsdk.json"
);
describe('saveCheckup', () => {
let adminStub, saveCheckup;
beforeAll(() => {
adminStub = jest.spyOn(admin, "initializeApp");
saveCheckup = require('../functions/save_checkup');
});
afterAll(() => {
adminStub.mockRestore();
testEnv.cleanup();
admin.database().ref("current").remove();
});
it("should save the user", async () => {
const wrapped = testEnv.wrap(saveCheckup);
await wrapped({attributes: {
date: "test date",
url: "testurl",
status: "200"
}});
const record = await admin.database().ref('/current/testurl').once('value');
expect(record.val()).toHaveProperty("status", "200");
})
});
Update: We were not able to solve this problem and ended up just writing offline tests for firestore instead.
The error output you posted shows that the error is within the Google Firebase node module files.
It even shows the line and character location:
at new FirestoreClient (node_modules/.../v1/firestore_client.js:113:32)
// Error location here ^
If you are trying to deploy locally please read this: https://firebase.google.com/docs/hosting/deploying
and follow the directions according to your situation.
Add a "jest.config.js" into the root directory keep the following code into the file
module.exports = {
testPathIgnorePatterns: ['lib/', 'node_modules/'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
testEnvironment: 'node'
};

Seed db with sequelize seed files before running tests with

How do I do rake style commands in my test file (Jest) with sequelize seeder files?
I'm trying to do the same thing as this, but with sequelize.
describe('routes : movies', () => {
beforeEach(() => {
return knex.migrate.rollback()
.then(() => { return knex.migrate.latest(); })
.then(() => { return knex.seed.run(); });
});
afterEach(() => {
return knex.migrate.rollback();
});
});
I know I'm late to answer this but I just spent the past week trying to solve this issue. I have been able to successfully do this using Sequelize in conjunction with their sister project, Umzug. You will have to read the documentation for your specific issue but I can copy my test file so you can get an idea of how I did it. I'm happy to help someone if they still struggle with it after looking at the files.
// account.test.js
const models = require('../models/index.js');
const migrations = require("../index");
beforeAll(async () => {
await migrations.up().then(function() {
console.log("Migration and seeding completed")
});
});
afterAll( async () => {
await migrations.down().then(function() {
console.log("Migrations and down seeding completed");
})
const users = await models.User.findAll();
expect(users).toStrictEqual([]);
});
describe("Integration Test", () => {
it("Account integration test", async () => {
const data = { userId: 1210}
const users = await models.User.findAll();
console.log("All users:", JSON.stringify(users, null, 2));
expect(users[0].firstName).toBe('John');
expect(data).toHaveProperty('userId');
});
});
My index.js file
// index.js
const config = require('/config/config.json');
const { Sequelize } = require('sequelize');
const { Umzug, SequelizeStorage } = require('umzug');
const sequelize = new Sequelize(config);
const umzugMigration = new Umzug({
migrations: { glob: 'migrations/*.js' },
context: sequelize.getQueryInterface(),
storage: new SequelizeStorage({ sequelize }),
logger: console,
});
const umzugSeeding = new Umzug({
migrations: { glob: 'seeders/*.js' },
context: sequelize.getQueryInterface(),
storage: new SequelizeStorage({ sequelize }),
logger: console,
});
module.exports.up = () => umzugMigration.up().then(() => umzugSeeding.up());
module.exports.down = () => umzugSeeding.down();
I think you shouldn't make real DB requests while testing your code. Mock your DB request and return the data set from your mock function if it's needed. Otherwise, it looks like you testing a library, in your case this lib is knex.
Read for more details regarding mocks https://jestjs.io/docs/en/mock-functions

Categories

Resources