Firebaseプロジェクトを本番用とテスト用で使い分けたい

# 趣旨

- Firebaseプロジェクトを本番用とテスト用で使い分けたい。
- Firebase CLIはデプロイの都合上、本番用のアカウントにログインさせたい。ただしテスト用のプロジェクトが別アカウントのものだとしてもログインし直すようなことは避けたい。
- そもそもFirebase CLIでログインしていなくても大抵の操作はできるようにしたい。
- テストにはJestを使いたい。

# 方法

サービスアカウントキーを使ってFirebase CLIでログインしていなくてもFirebaseの各種サービスを扱えるようにする。

---

# 手順

## テスト用プロジェクトのサービスアカウントキーを参照可能な場所に置く

Firebase Consoleから取得できるサービスアカウントキーをプロジェクト内の適当なフォルダに置きます。当然のことですが**このファイルはGitHubにプッシュしないようにします。**

(ref: https://firebase.google.com/docs/admin/setup?hl=ja )

```json:secrets/serviceAccountKey.test.json
{
  "type": "service_account",
  "project_id": ".....",
  "private_key_id": ".....",
  "private_key": "-----BEGIN PRIVATE KEY-----\n.....",
  "client_email": ".....",
  "client_id": ".....",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/....."
}
```

## サービスアカウントキーがどこにあるかを示したJSONファイルを作る

この後に紹介するJavaScriptが読みに行くJSONを作ります。必要なのはサービスアカウントキーへのファイルパスです。

```json:functions/env.test.json
{
  "GOOGLE_APPLICATION_CREDENTIALS": "../secrets/serviceAccountKey.test.json"
}
```

## サービスアカウントキーを読み取って環境変数を登録するJavaScriptを書く

サービスアカウントキーファイルを読み取れればProjectIDがわかるので、そこから必要な環境変数 `GOOGLE_APPLICATION_CREDENTIALS`, `FIREBASE_CONFIG`, `GCLOUD_PROJECT` を生成できます。

```js:functions/scripts/load-test-env.js
/*
 * 下記の環境変数を設定する。
 * GOOGLE_APPLICATION_CREDENTIALS
 * FIREBASE_CONFIG
 * GCLOUD_PROJECT
 */

const path = require('path');
const fs = require('fs');

const ENV_JSON = 'env.test.json';

(() => {
  if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
    const envJsonPath = path.join(path.resolve(), ENV_JSON);
    if (fs.existsSync(envJsonPath)) {
      const env = require(envJsonPath);
      if (env.GOOGLE_APPLICATION_CREDENTIALS) {
        process.env.GOOGLE_APPLICATION_CREDENTIALS = env.GOOGLE_APPLICATION_CREDENTIALS;
      }
    }
  }

  if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
    return;
  }

  if (!process.env.FIREBASE_CONFIG) {
    const serviceAccountKeyPath = path.join(path.resolve(), process.env.GOOGLE_APPLICATION_CREDENTIALS);
    if (fs.existsSync(serviceAccountKeyPath)) {
      const serviceAccount = require(serviceAccountKeyPath);
      const projectId = serviceAccount.project_id;
      if (projectId) {
        const databaseURL = `https://${projectId}.firebaseio.com`;
        const storageBucket = `${projectId}.appspot.com`;

        process.env.FIREBASE_CONFIG = JSON.stringify({
          projectId,
          databaseURL,
          storageBucket
        });
      }
    }
  }

  if (!process.env.FIREBASE_CONFIG) {
    return;
  }

  if (!process.env.GCLOUD_PROJECT) {
    process.env.GCLOUD_PROJECT = JSON.parse(process.env.FIREBASE_CONFIG).projectId;
  }
})();
```

## Jestの実行時に環境変数を登録するスクリプトを実行する

Jestによるテストを実行するときに環境変数が登録されていることを担保できます。

```json:functions/package.json
  "jest": {
    (省略)
    "setupFiles": [
      "<rootDir>/scripts/load-test-env.js"
    ]
  }
```

## まとめ

これにより本番用とテスト用のプロジェクトを別々に管理しつつ、テスト用のプロジェクトが**現在Firebase CLIでログインしているアカウントとは別のアカウントのものだとしても**問題なく使い分けることができます。