エミュレータ環境でFirestoreのセキュリティルールをテストするまでの準備

エミュレータ環境でFirestoreのセキュリティルールをテストするまでの準備

October 24, 2021

もう一度同じ作業をすることになった時のために。自分用の備忘録です。

環境準備 #

(Firebase CLI でプロジェクト作成は済んでいるものとしてスタート)

ディレクトリを作成する #

firestore.rules の隣に tests ディレクトリを作成する。

├ firestore.rules
└ tests/

以降は tests ディレクトリの中で作業する。

パッケージをインストールする #

package.json を作成して以下を記載する。

{
  "private": true,
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watchAll"
  },
  "devDependencies": {
    "@firebase/testing": "^0.20.11",
    "@types/jest": "^27.0.1",
    "jest": "^27.1.0",
    "ts-jest": "^27.0.5",
    "typescript": "^4.4.2"
  }
}

パッケージをインストールする。

npm install

この記事を参考に作業しているタイミングでは、(記事作成時点の)上記からパッケージに更新が入っていると思うので最新版にしておく。

以下を実施し更新の有無を確認する。

npx npm-check-updates

更新点があれば以下を実施する。

npx npm-check-updates -u -i
npm install

tsconfig.json を作成する #

tsconfig.json

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true
  }
}

jest.config.js を作成する #

jest.config.js

module.exports = {
  verbose: true,
  preset: 'ts-jest',
  testEnvironment: 'node',
  moduleFileExtensions: ['js', 'ts'],
  transform: { '^.+\\.ts$': 'ts-jest' },
};

テスト実施 #

テストファイルを作成する #

__tests__ ディレクトリを作成後、配下に example.test.ts を作成し以下を記載する。

example.test.ts

import fs from 'fs/promises';
import * as firebase from '@firebase/testing';

const PROJECT_ID = 'firestore-testing';
const RULES_FILE = '../firestore.rules';

beforeAll(async () => {
  const rules = await fs.readFile(RULES_FILE, 'utf8');
  await firebase.loadFirestoreRules({ projectId: PROJECT_ID, rules });
});

afterEach(async () => {
  await firebase.clearFirestoreData({ projectId: PROJECT_ID });
});

afterAll(async () => {
  await Promise.all(firebase.apps().map((app) => app.delete()));
});

const createAuthApp = (auth?: object) => {
  return firebase.initializeTestApp({ projectId: PROJECT_ID, auth: auth }).firestore();
};

const authedDB = createAuthApp({ uid: 'alice', email: 'alice@example.com' });
const unAuthedDB = createAuthApp();
const authedUser = authedDB.collection('users');
const unAuthedUser = unAuthedDB.collection('users');

describe('/users/{id}', () => {
  test('list succeeds', async () => {
    await firebase.assertSucceeds(authedUser.get());
  });
  test('list fails', async () => {
    await firebase.assertFails(unAuthedUser.get());
  });
  test('get succeeds', async () => {
    await firebase.assertSucceeds(authedUser.doc('01234567890123456789').get());
  });
  test('get fails', async () => {
    await firebase.assertFails(unAuthedUser.doc('01234567890123456789').get());
  });
});

セキュリティルールを編集する #

確認のために firestore.rules を以下の内容にする。

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /users/{id} {
      allow list: if request.auth != null;
      allow get: if request.auth != null;
    }
  }
}

テストを実行する #

npm test

テストが実施されて以下の表示になれば成功。

PASS  __tests__/example.test.ts (*.** s)
  /users/{id}
    ✓ list succeeds (** ms)
    ✓ list fails (** ms)
    ✓ get succeeds (** ms)
    ✓ get fails (** ms)

Test Suites: 1 passed, 1 total
Tests:       4 passed, 4 total
Snapshots:   0 total
Time:        *.*** s
Ran all test suites.

以上 #

最終的なディレクトリ構成は以下。

├ firestore.rules
└ tests/
  ├ package.json
  ├ package-lock.json
  ├ node_modules/
  ├ tsconfig.json
  ├ jest.config.js
  └ __tests__
     └ example.test.ts

参考 #