Can't perform any unit tests for firestore security rules with jest

Viewed 504

I am new to testing and I am looking at firebase documentation for testing security rules, but it's very limiting and has no information.

this is my security rule that I want to test:

service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}{
      allow read: if belongsTo(userId) && isLoggedIn()
    }
  }

  function isLoggedIn() {
    return request.auth.uid != null
  }
  function belongsTo(userId){
    return request.auth.uid == resource.data.uid
  }

This is my test file:

import {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment,
  RulesTestEnvironment,
} from "@firebase/rules-unit-testing";
import { readFileSync } from "fs";

// Assuming a Firestore app and the Firestore emulator for this example
import { setDoc } from "firebase/firestore";

test("this is test, checks that only authenticated user can see their data", async () => {
  let testEnv = await initializeTestEnvironment({
    projectId: "myproject-ce845",
    firestore: {
      rules: readFileSync("firestore.rules", "utf8"),
      port: 8080,
    },
  });
  const alice = testEnv.authenticatedContext("alice");
  await assertSucceeds(setDoc(alice.firestore(), "/users/alice"));
});

this is the error that I'm getting:

    {"error":{"code":400,"message":"Error compiling rules:\nL29:5 missing ';' at 'return'\nL33:3 mismatched input '}' expecting {'&&', ',', '.', '==', '>', '>=', '[', '<', '<=', '-', '%', '!=', '||', '+', ']', '\/', '*', '?', 'in', 'is'}","status":"INVALID_ARGUMENT"}}
1 Answers

The error you are seeing (INVALID_ARGUMENT) is a syntax error with your rules. I’m not sure if it’s a typo on your question or not, but your rules are missing a closing bracket at the end of the file:

rules_version = '2'; // <= Also the rule syntax version should be added
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{userId}{
      allow read: if belongsTo(userId) && isLoggedIn();
    }
  }

  function isLoggedIn() {
    return request.auth.uid != null;
  }
  function belongsTo(userId){
    return request.auth.uid == resource.data.uid;
  }
} // <= This closing bracket is missing

In addition to that, I think it’s important to read this issue if you are attempting to follow the documentation about building unit tests that targets your rules. This issue affects using ES6 modules, and using the modular syntax will lead to this error:

FirebaseError: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore

A workaround shown in the issue is to create your tests using CommonJS syntax, or try with the V8 SDK. I went ahead and built an example based on the workaround shown:

const { initializeTestEnvironment, assertSucceeds, assertFails} = require("@firebase/rules-unit-testing");
const { doc, getDoc } = require("firebase/firestore");
const fs = require('fs');

const testRules = async () => {
    const testEnv = await initializeTestEnvironment({
        projectId: projectIDStr,
        firestore: {
            rules: fs.readFileSync(rulesPath, "utf8"),
            host: "localhost", // Required to run in the emulator in case it’s not automatically detected!
            port: 8080
        },
    });
    const context = await testEnv.authenticatedContext("id_alice");
    return await assertSucceeds(getDoc(doc(context.firestore(), "users", "id_alice")));
}

test("Simulated read is allowed by Firebase rules", async () => {
    await expect(testRules()).resolves.toBeDefined(); // assertSucceeds promise is resolved since rules allowed it
});

This test aligns more with your rules, as only authenticated users are able to see their data.

Related