A month of Flutter: set up Firestore rules tests

One aspect of using Firestore for my data backend means I need to be certain my security rules are configured correctly. Otherwise users might be able to read or write date they shouldn't have access to.

A few days ago I set up Firestore in the server directory. I'm going to continue that work and configure tests to run on the Firestore emulator based off of the typescript-qickstart example.

In package.json I'll add some devDependencies and define several scripts. Node package scripts can be run with npm run <name>.

  • postinstall will set up the Firestore emulator after npm install is run in the scripts directory
  • start-emulator will will do just that
  • pretest will compile the project's TypeScript files before the test script is run
  • test will run the actual tests within the test directory using mocha test runner
  • posttest will cleanup the test *.js and *.js.map files created during pretest
  • ci uses a handy Node package start-server-and-test to start the emulator, wait for it to be ready, run the tests, and then shut down

I created a new tsconfig.json file with npx tsc --init. The two main changes I made were to target es6 instead of es5 and enable experimentalDecorators for the mocha-typescript package.

Within test/firestore.ts I'm defining a FirestoreTest class that will handle loading the rules, and setting up and tearing down test databases. mocha-typescript will use a new instance of this class for each test. Each instance will use a different projectId to avoid different test runs from interfering with each other.

The Cloud Firestore emulator persists data. This might impact your results. To run tests independently, assign a different project ID for each, independent test. When you call firebase.initializeAdminApp or firebase.initializeTestApp, append a user ID, timestamp, or random integer to the projectID.

Test your security rules

I changed firestore.rules so there was an allowed rule and a denied rule. These will be updated with real rules before the next deploy.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read: if true;
      allow write: if false;
    }
  }
}

The initial tests for the user collection in user_rules_test.ts look like this:

@suite
class Users extends FirestoreTest {
  @test
  async 'can read'() {
    const user = this.db().collection('users').doc('alice');
    await firebase.assertSucceeds(user.get());
  }

  @test
  async 'can not write'() {
    const user = this.db().collection('users').doc('alice');
    await firebase.assertFails(user.set({ nickname: 'alice' }));
  }
}

The @suite, @test, and class style is supported by mocha-typescript. One of the reasons I chose TypeScript instead of JavaScript is because the types are similar to Dart much of the time.

I created a success test and a failure test to as proof of concept while setting up all the tooling. They get a database handle and assert that it can be read or written to.

I can now run npm run ci and see the following:

  Users
    ✓ can read (162ms)
    ✓ can not write (95ms)
  2 passing (462ms)

The next step will be to get these tests running along with the existing CI.

Code changes

Posts in this series

  • A month of Flutter
  • A month of Flutter: create the app
  • A month of Flutter: configuring continuous integration
  • A month of Flutter: continuous linting
  • A month of Flutter: upgrading to 1.0
  • A month of Flutter: initial theme
  • A month of Flutter: no content widget
  • A month of Flutter: a list of posts
  • A month of Flutter: extract post item widget
  • A month of Flutter: post model and mock data
  • A month of Flutter: rendering a ListView with StreamBuilder
  • A month of Flutter: Stream transforms and failing tests
  • A month of Flutter: real faker data
  • A month of Flutter: rendering network images
  • A month of Flutter: FABulous authentication
  • A month of Flutter: configure Firebase Auth for Sign in with Google on Android
  • A month of Flutter: configure Firebase Auth for Sign in with Google on iOS
  • A month of Flutter: Sign in with Google
  • A month of Flutter: mocking Firebase Auth in tests
  • A month of Flutter: delicious welcome snackbar
  • A month of Flutter: navigate to user registration
  • A month of Flutter: user registration form
  • A month of Flutter: testing forms
  • A month of Flutter: setting up Firebase Firestore
  • A month of Flutter: awesome adaptive icons
  • A month of Flutter: set up Firestore rules tests
  • A month of Flutter: Firestore create user rules and tests
  • A month of Flutter: WIP save users to Firestore
  • A month of Flutter: user registration refactor with reactive scoped model
  • A month of Flutter: the real hero animation
  • A month of Flutter: a look back