Firestore本地模拟器中删除与更新文档安全规则测试问题排查
问题分析与解决方案
首先,咱们先揪出最可能的核心问题——安全规则的路径匹配和你测试的文档路径不匹配!
看你的安全规则:
match /posts/{postId} { allow read: if true; allow create: if true; allow delete, update: if (resource.data.user.uid == request.auth.uid) }
这个规则是匹配根集合下的posts文档,但你的测试代码里操作的是嵌套子集合:
const testDoc = db .collection("general") .doc("spaces") .collection("spaces") .doc("9LbUetZxWeL1ln5hv9ug") .collection("posts") .doc("RtUnezdAgXUfWmRSPpkF");
也就是这个文档的完整路径是general/spaces/spaces/9LbUetZxWeL1ln5hv9ug/posts/RtUnezdAgXUfWmRSPpkF,你的安全规则根本没覆盖到这个嵌套层级的posts子集合,所以模拟器默认拒绝了操作。而你说硬编码路径能成功,应该是硬编码的路径刚好和规则里的根级posts匹配了。
另外,还有一个容易忽略的点——Firebase测试模拟器的环境隔离性:
你在模拟器UI里手动添加的文档,不会被测试用例访问到,因为每个测试用例都会初始化一个干净的独立环境。哪怕你在UI里看到文档存在,测试代码里的数据库其实是空的,除非你在测试代码里先创建这个文档并设置好user.uid字段。
具体修复步骤:
调整安全规则匹配正确的路径
你需要修改规则来覆盖嵌套子集合的posts,有两种方式:- 精准匹配你的嵌套路径:
match /general/spaces/spaces/{spaceId}/posts/{postId} { allow read: if true; allow create: if true; allow delete, update: if resource.data.user.uid == request.auth.uid; } - 如果有多个层级的
posts子集合,可以用递归匹配(注意安全性,只在你需要所有posts子集合用同一规则时使用):match /{path=**}/posts/{postId} { allow read: if true; allow create: if true; allow delete, update: if resource.data.user.uid == request.auth.uid; }
- 精准匹配你的嵌套路径:
在测试用例中初始化目标文档
因为测试环境是隔离的,你需要在测试前先创建文档并设置正确的user.uid,确保和request.auth.uid一致:it("If creator, Allow delete/update items in the post/comment collection", async () => { const db = firebase .initializeTestApp({ projectId: MY_PROJECT_ID, auth: myAuth }) .firestore(); const testDoc = db .collection("general") .doc("spaces") .collection("spaces") .doc("9LbUetZxWeL1ln5hv9ug") .collection("posts") .doc("RtUnezdAgXUfWmRSPpkF"); // 先创建文档并设置匹配的user.uid await testDoc.set({ user: { uid: myAuth.uid } }); // 再执行删除断言 await firebase.assertSucceeds(testDoc.delete()); });验证规则加载是否正确
确保你在测试前正确加载了安全规则,比如在测试文件开头添加:beforeAll(async () => { await firebase.loadFirestoreRules({ projectId: MY_PROJECT_ID, rules: fs.readFileSync("path/to/your/firestore.rules", "utf8") }); });避免测试用的是旧规则或者默认规则。
按照这几步调整后,应该就能解决你的问题了!
内容的提问来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

