Non Default Ports Do Not work
I know this is a bug report and I shouldn't let my frustrations bleed through, but after spending weeks trying to get a WSL fix only to be forced to downgrade to WSL 1, I was pretty frustrated that I encountered another error so quickly. It has been weeks and countless hours trying to make it through a simple codelab.
Platform: Windows Build 19042.630 w/ WSL Version 1 Ubuntu 20.04 References: Initially I came across this issue while trying to follow Todd/Rachel's Youtube Video on Unit Security Testing Issue 61
Description: I did a clean setup for the emulators code lab and followed the instructions to step 8. The only change that I had to make is that my default ports are not free, so I had to modify the firebase.json file before running the emulators
{
"firestore": {
"rules": "firestore.rules",
"indexes": "firestore.indexes.json"
},
"functions": {
"source": "functions"
},
"hosting": {
"public": "public",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
]
},
"emulators": {
"functions": {
"port": 5002
},
"firestore": {
"port": 8085
},
"hosting": {
"port": 5007
}
}
}
During step 8 when I go to run the mocha tests I get the following error:
Error: Timeout of 5000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.
Based on my experimenting on my referenced issue, I believe that the mocha test doesn't know how to connect to the emulator on non-default ports. I had a work around for initializeTestApp, but could not find a workaround for initializeAdminApp.
@b00t you've run into a lot of bugs and issues trying to use Firebase, so your frustration is 100% warranted. Thank you for continuing to file feedback.
The firebase.json file is only used by the Firebase CLI. Any other libraries which interact with the emulators, such as @firebase/rules-unit-testing or firebase-admin find the emulators through the FIREBASE_<FOO>_EMULATOR_HOST environment variables not through the firebase.json file.
You can either set these yourself, or you can run your tests through firebase emulators:exec 'npm run test' which will automatically set the right environment variables.
I will see if I can improve the error messages in this case so that future users don't get stuck.
Just to echo @samtstern, this is great feedback; thanks.
Looks like we already have warnings in the library: https://github.com/firebase/firebase-js-sdk/blob/master/packages/rules-unit-testing/src/api/index.ts#L216
Did you see anything in your logs like:
Warning: FIRESTORE_EMULATOR_HOST not set, using default value localhost:8080
If not, maybe mocha is hiding those logs?
@rachelmyers Thanks.
@samtstern You are correct in that error appearing. The issue is I couldn't find any documentation on how to set the value. I also asked on other unofficial support channels (e.g. Slack) and searched r/firebase as well as stackoverflow. Given I'm very new to web programming so likely didn't understand the documentation properly.
When trying to follow Rachel and Todd's video on unit security rules ( Issue 61 ) eventually I was able to figure out a way to modify the function Todd wrote to change the port being used
const EMULATOR_PORT = 8086
function getFirestore(auth) {
const db = firebase.initializeTestApp({projectId: MY_PROJECT_ID, auth: myAuth }).firestore();
db.useEmulator("localhost", EMULATOR_PORT)
return db
}
This seemed to work for this use case although I still got the error with
Warning: FIRESTORE_EMULATOR_HOST not set, using default value localhost:8080
The issue was that useEmulator does not work for the Admin SDK
function getAdminFirestore() {
const db = firebase.initializeAdminApp({projectId: MY_PROJECT_ID }).firestore();
db.useEmulator("localhost", EMULATOR_PORT)
return db
}
As you would get the error: ERROR: TypeError: db.useEmulator is not a function
Note, I probably tried things that seem ridiculous to use based on the documentation I tried Attempt 1: const FIRESTORE_EMULATOR_HOST = "localhost:8085"
Attempt 2: export FIRESTORE_EMULATOR_HOST = "localhost:8085"
Attempt 3: const db = firebase.initializeAdminApp({projectId: MY_PROJECT_ID, FIRESTORE_EMULATOR_HOST: "localhost:8085").firestore();
However none of those methods seemed to work.
Based on the recent post from sam
The firebase.json file is only used by the Firebase CLI. Any other libraries which interact with the emulators, such as @firebase/rules-unit-testing or firebase-admin find the emulators through the FIREBASE_<FOO>_EMULATOR_HOST environment variables not through the firebase.json file.
You can either set these yourself, or you can run your tests through firebase emulators:exec 'npm run test' which will automatically set the right environment variables.
It looks like you may actually set the env variables outside of the code in the shell, however I have no idea how to set them or manage them.
One last final aside: In the youtube video you may want to add a note about updating to const firebase = require('@firebase/rules-unit-testing'); I had issues when I originally followed the const firebase = require('@firebase/testing')
@b0ot thanks for walking us through what you tried. It's really clear that we're making too many assumptions about what you and other web developers understand! We need to be much clearer.
Let me explain a bit:
FIRESTORE_EMULATOR_HOST is an environment variable. They are set in your terminal shell and can be accessed by programs you run from the command line, so they're popular in server environments. Using all caps and underscores is a convention for environment variables. Another common one you may run into is GOOGLE_APPLICATION_CREDENTIALS which all Google Cloud SDKs will use to locate credentials. Environment variables (env vars) are used to allow configuring server applications from the outside without requiring code changes.
Here's a super quick example of setting and using one:
$ MESSAGE="Hello world"
$ echo "$MESSAGE"
Hello world
So if you want to set one for your unit tests, you can just do it when you run the command:
FIRESTORE_EMULATOR_HOST=localhost:8088 npm run test
When you run firebase emulators:exec we set the environment for you before running your command.
Drive-by comment in case anyone want this: If you have to set it within your code in Node.js (which we don't recommend), process.env.FIRESTORE_EMULATOR_HOST = "localhost:8085"; is the way to go. Make sure that is above any calls to the SDKs.
@samtstern thanks for the quick reply and @yuchenshi for the additional information.
I was able to get it to run successfully by exporting the env variable in my shell (bash)!
I wasn't able to get it to work with firebase emulators:exec however from what I can determine it is because my emulators were setup in a parent folder above my test folder where mocha was installed.
I believe there is probably a way I could modify the package.json script files to make it work, but haven't figured it out currently. Main folder doesn't know about mocha, test folder wasn't starting the emulators correctly. I may experiment a bit more, but for now I'm happy that I have a way to make it work.
Eventually I'll make it through this codelab ... after all it says I only have 33 minute remaining :)
@b0ot we seriously appreciate your patience and this thread has spawned a whole bunch of internal conversation about how we can make this easier and clearer. So your bad experience will at least help the next person!
We're fixing this issue with two new methods here: https://github.com/firebase/firebase-js-sdk/pull/4388
Once that is merged and released, we'll update the docs to recommend them.
I was also very confused about how to set up the environment variable correctly. I just want to share my use case in case it helps anyone. I'm running an app on Nextjs and I'm fetching data on the server using the admin sdk. On Nextjs you set up local environments in a .env.local file. I tried to paste export FIRESTORE_EMULATOR_HOST="localhost:8080" on my terminal but what I understand is that it creates a temporary environment variable.
As mentioned here, for permanent setting, you need to understand where to put the “export” script. Where here means Bash Shell Startup Script like /etc/profile, ~/.bash_profile, ~/.bashrc. But frankly I never really understood that part.
So in my case, what I ended up doing is:
if (process.env.NEXT_PUBLIC_DB_HOST === 'localhost') {
process.env.FIRESTORE_EMULATOR_HOST = 'localhost:8080';
}
In my package.json I used the following and it works:
"test": " FIRESTORE_EMULATOR_HOST=localhost:8088 FIREBASE_DATABASE_EMULATOR_HOST=localhost:9000 jest --watchAll --detectOpenHandles"