Cloud Firestore and Realtime Database both rely on powerful, concise rules languages
specifically created to govern information security and access control. However,
as rules get longer and more complex, you might need some help debugging errors
in their behavior.
The Firebase Emulators include the ability to generate rule coverage reports, so you
can see see exactly what each subexpression evaluated to when you reproduce
an error. The reports also provide information about how frequently each test
case used a rule, like traditional "line coverage" techniques.
Generate a report
After running a suite of tests, you can access test
coverage reports that show how each of your security rules was evaluated.
To get the reports, query an exposed endpoint on the emulator while
it's running. For a browser-friendly version, use the following URL:
Cloud Firestore
http://localhost:8080/emulator/v1/projects/
<database_name>
:ruleCoverage.html
Realtime Database
http://localhost:9000/.inspect/coverage?ns=
<database_name>
This breaks your rules into expressions and subexpressions that you can
mouseover for more information, including number of evaluations and values
returned. For the raw JSON version of this data, include the following URL
in your query:
Cloud Firestore
http://localhost:8080/emulator/v1/projects/
<database_name>
:ruleCoverage
Realtime Database
http://localhost:9000/.inspect/coverage.json?ns=
<database_name>
Debugging example rules
To easily generate a test report, use the emulator quickstarts available on
GitHub for
Cloud Firestore
and
Realtime Database
.
These quickstarts guide you through properly installing
and initializing the emulators, then generating sample tests from an example
set of rules.
Consider an example app using Cloud Firestore that counts how many times users
click a button. The app employs the following rules:
Cloud Firestore
service cloud.firestore {
match /databases/{database}/documents {
match /counters/{counter} {
allow read;
allow write: if request.resource.data.value == resource.data.value +1;
}
}
}
To debug the errors in the rules shown above, use the following sample
JavaScript test:
const counter0 = db.collection("counters").doc("0");
await firebase.assertSucceeds(counter0.set({value: 0}));
The emulator generates a report available at the URL noted above:
http://localhost:8080/emulator/v1/projects/
<database_name>
:ruleCoverage.html
The report shows the following undefined and null-value errors:
The problem with this specific example is that the rules don't differentiate
between creating the document and updating the document. Consequently, the
write isn't allowed if the document doesn't exist, and the document can't be
created because it doesn't exist. Differentiating the "write" into two
more specific operations ? "create" and "update" ? solves the problem.
Cloud Firestore
service cloud.firestore {
match /databases/{database}/documents {
match /counters/{counter} {
allow read;
allow create: if request.resource.data.value == 0;
allow update: if request.resource.data.value == resource.data.value +1;
}
}
}
The generated report shows how frequently each rule was used and what was
returned.