【问题标题】:How to setup test data when testing Firestore Rules with Emulator?使用模拟器测试 Firestore 规则时如何设置测试数据?
【发布时间】:2019-10-09 14:34:37
【问题描述】:

我正在使用 mocha 和 Firestore Emulator 对 Cloud Firestore 规则进行测试,问题是如何在运行测试之前初始化一些测试数据?

为了测试我的规则,我首先需要初始化一些测试数据。问题是使用 Emulator 时我无法将任何数据放入文档中,文档只有 id。 我没有找到任何在the docs 中为规则测试设置测试数据的示例,所以我尝试同时使用两者 makeDocumentSnapshot 来自@firebase/testing 并通过使用initializeAdminApp 创建的管理应用 创建文档。

用例

要访问/objects/{object_id} 的文档,用户必须经过身份验证并拥有read 权限:get('/objects/{object_id}/users/{$(request.auth.uid)}').data.read == true。此外,object 必须可用:get('/objects/{object_id}').data.available == true

所以,为了测试我的规则,我需要一些具有用户权限的预设测试数据。

预期的数据库结构

objects collection:
  object_id: {
    // document fields:
    available (bool)

    // nested collection:
    users collection: {
      user_id: {
        // document fields:
        read (bool)
      }
    }
  }

我的规则示例

service cloud.firestore {
  match /databases/{database}/documents {
    match /objects/{object} {
      function objectAvailable() {
        return resource.data.available;
      }
      // User has read access.
      function userCanReadObject() {
        return get(/databases/$(database)/documents/objects/$(object)/users/$(request.auth.uid)).data.read == true;
      }
      // Objects Permission Rules
      allow read: if objectAvailable() && userCanReadObject();
      allow write: if false;

      // Access forbidden. Used for permission rules only.
      match /users/{document=**} {
        allow read, write: if false;
      }
    }
  }
}

我的测试示例

const firebase = require('@firebase/testing');
const fs = require('fs');

// Load Firestore rules from file
const firestoreRules = fs.readFileSync('../firestore.rules', 'utf8');
const projectId = 'test-application';
const test = require('firebase-functions-test')({ projectId, databaseName: projectId });

describe('Tests for Rules', () => {
  let adminApp;

  const testData = {
    myObj: {
      id: 'test',
      data: {
        available: true,
      },
    },
    alice: {
      id: 1,
      data: {
        read: true,
      },
    },
  };

  before(async () => {
    // Load Rules
    await firebase.loadFirestoreRules({ projectId,  rules: firestoreRules });

    // Initialize admin app.
    adminApp = firebase.initializeAdminApp({ projectId }).firestore();

    // Create test data
    await adminApp.doc(`objects/${testData.myObj.id}`).set(testData.myObj.data);
    await adminApp
      .doc(`objects/${testData.myObj.id}/users/${testData.alice.id}`)
      .set(testData.alice.data);

    // Create test data with  `firebase-functions-test`
    // test.firestore.makeDocumentSnapshot(testData.myObj.data, `objects/${testData.myObj.id}`);
    // test.firestore.makeDocumentSnapshot(
    //   testData.alice.data,
    //   `objects/${testData.myObj.id}/users/${testData.alice.id}`,
    // );
  });

  beforeEach(async () => {
    await firebase.clearFirestoreData({ projectId });
  });

  after(async () => {
    // Shut down all testing Firestore applications after testing is done.
    await Promise.all(firebase.apps().map(app => app.delete()));
  });

  describe('Testing', () => {
    it('User with permission can read objects data', async () => {
      const db = firebase
        .initializeTestApp({ projectId, auth: { uid: testData.alice.id } })
        .firestore();
      const testObj = db.doc(`objects/${testData.myObj.id}`);

      await firebase.assertSucceeds(testObj.get());
    });
  });
});

测试运行的控制台输出:

1) User with permission can read objects data
0 passing (206ms)
1 failing
1) Tests for Rules
 Testing
   User with permission can read objects data:
FirebaseError: 
false for 'get' @ L53

为了检查创建的测试数据,我在await firebase.assertSucceeds(testObj.get()); 行之前添加了以下代码:

const o = await adminApp.doc(`objects/${testData.myObj.id}`).get();
const u = await adminApp.doc(`objects/${testData.myObj.id}/users/${testData.alice.id}`).get();
console.log('obj data: ', o.id, o.data());
console.log('user data: ', u.id, u.data());

输出如下:

obj data:  test undefined
user data:  1 undefined

我也试过把beforeEach的代码去掉,结果还是一样。

【问题讨论】:

  • 你让它工作了吗?我觉得没问题。

标签: firebase google-cloud-firestore firebase-security firebase-cli


【解决方案1】:

您可以使用initializeAdminApp 获取管理员权限(允许所有操作):


    const dbAdmin = firebase.initializeAdminApp({projectId}).firestore();

    // Write mock documents
    if (data) {
        for (const key in data) {
            if (data.hasOwnProperty(key)) {
                const ref = dbAdmin.doc(key);
                await ref.set(data[key]);
            }
        }
    }

数据应该具有以下格式:

  data = {
    'user/alice': {
      name:'Alice'
    },
    'user/bob': {
      name:'Bob'
    },
  };

【讨论】:

    【解决方案2】:

    您必须在应用规则之前添加数据。

    详细信息可以找here

    const firebase = require('@firebase/testing');
    const fs = require('fs');
    let db
    let projectId = `my-project-id-${Date.now()}`
    
    async function setup(auth) {
      const app = await firebase.initializeTestApp({
        projectId: projectId,
        auth: auth
      });
    
      db = app.firestore();
    
      let data = {
        'users/alovelace': {
          first: 'Ada',
          last: 'Lovelace'
        }
      }
    
      // Add data before apply rules
      for (const key in data) {
        const ref = db.doc(key);
        await ref.set(data[key]);
      }
    
      // Apply rules
      await firebase.loadFirestoreRules({
        projectId,
        rules: fs.readFileSync('firestore.rules', 'utf8')
      });
    }
    
    test('logged in', async () => {
      await setup({ uid: "alovelace" })
    
      let docRef = db.collection('users');
    
      // check if there is data
      let users = await docRef.get()
      users.forEach(user => {
      console.warn(user.id, user.data())
      });
    
      let read = await firebase.assertSucceeds(docRef.get());
      let write = await firebase.assertFails(docRef.add({}));
    
      await expect(read)
      await expect(write)
    });
    
    afterAll(async () => {
      Promise.all(firebase.apps().map(app => app.delete()))
    });
    

    firestore.rules

    service cloud.firestore {
      match /databases/{database}/documents {
        match /{document=**} {
           allow read:if request.auth.uid != null;
           allow write: if false
        }
      }
    }
    

    【讨论】:

    • 是这么想的,但是在加载规则之前尝试在before()statement 中添加模拟数据,仍然得到Permission denied,没有匹配的allow 语句。 firebase 默认是不允许访问吗?
    • 最新版本的 Firebase 测试会自动加载环境的规则文件。我通过在我的测试目录中创建一个管理规则文件来解决这个问题,然后在为数据库播种之前将其加载到测试设置中,然后切换回我正在测试的 firestore.rules 文件。
    • 非常幼稚的问题:您实际上是如何运行测试的?你使用什么命令?
    • 使用 firebase serve --only firestore 启动 firestore 模拟器,并使用 'jest' 或其他测试运行器运行测试。
    猜你喜欢
    • 2019-08-13
    • 2020-06-17
    • 2020-11-07
    • 2018-12-31
    • 2020-06-19
    • 1970-01-01
    • 2020-04-04
    • 2021-03-29
    • 1970-01-01
    相关资源
    最近更新 更多