单元测试Firebase Storage安全规则时上传文件卡住如何排查
Firebase Storage规则测试上传卡住问题排查
问题背景
我配置了一个支持用户上传音频文件的存储桶,目标是验证其权限规则是否符合预期。但在使用@firebase/rules-unit-testing库向模拟器上传测试文件时,上传进程一直卡住无任何进展。
安全规则配置
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if false; } match /audio/{audioFileID} { allow create: if request.auth != null && request.resource.contentType.matches('audio/(flac|wav)'); } } }
测试代码
import { assertFails, assertSucceeds, initializeTestEnvironment, RulesTestEnvironment, } from "@firebase/rules-unit-testing"; import { test, beforeAll, beforeEach, afterAll } from "vitest"; import fs from "fs"; const createTestFile = (size: number) => Buffer.alloc(size); let testEnv: RulesTestEnvironment; beforeAll(async () => { testEnv = await initializeTestEnvironment({ projectId: "stst-et-interviewer-dev", hub: { host: "localhost", port: 4400, }, }); }); beforeEach(async () => { await testEnv.clearStorage(); await testEnv.clearFirestore(); }); afterAll(async () => await testEnv.cleanup()); const loadStepStoneImage = () => fs.readFileSync("./public/images/stepstoneLogo.svg"); test("Storage does not allow you to read files", async () => { const rouge = testEnv.unauthenticatedContext(); const alice = testEnv.authenticatedContext("alice"); await assertFails( rouge.storage().ref("audio/test-interview.flac").getDownloadURL() ); await assertFails( alice.storage().ref("audio/test-interview.flac").getDownloadURL() ); await assertFails(alice.storage().ref("audio/").listAll()); }); test("Storage does not allow you to upload a file if you are not logged in", async () => { const rouge = testEnv.unauthenticatedContext(); const upload = rouge .storage("stst-et-interviewer-dev.appspot.com") .ref("audio/logo.svg") .put(createTestFile(200), { contentType: "audio/flac" }); await assertFails(upload.then()); // <- 在此处卡住 console.log("该日志不会打印"); });
可能原因
- 模拟器配置缺失:初始化测试环境时仅配置了hub端口4400,未显式指定Storage模拟器配置,SDK无法正确路由Storage请求到对应服务端口(默认Storage模拟器端口为9199),导致请求一直挂起。
- Promise写法错误:
upload.then()未传入任何回调函数,返回的Promise永远不会进入决议状态,assertFails接收始终pending的Promise就会一直卡住。 - 存储桶名称不匹配:测试代码中手动指定存储桶为
stst-et-interviewer-dev.appspot.com,如果模拟器实际启动的默认存储桶名称和该值不一致,上传请求会被转发到不存在的桶,无法得到响应。 - 版本兼容问题:本地安装的
@firebase/rules-unit-testing版本和Firebase CLI版本差距过大,Storage模拟器的API逻辑和SDK调用逻辑不匹配,导致请求无法正常返回。
调试方法
- 先访问Firebase模拟器控制台(默认地址
http://localhost:4000),确认Storage模拟器是否正常启动,记录其运行端口和默认存储桶名称,核对测试代码中的配置是否一致。 - 给上传任务添加catch回调和超时逻辑,跳过
assertFails单独运行上传逻辑,获取明确的错误信息:
const upload = rouge .storage("stst-et-interviewer-dev.appspot.com") .ref("audio/logo.svg") .put(createTestFile(200), { contentType: "audio/flac" }); upload.then(res => console.log("上传成功", res)).catch(err => console.error("上传报错", err)); const timeout = new Promise((_, reject) => setTimeout(() => reject(new Error("请求超时")), 5000)); await Promise.race([upload, timeout]);
- 初始化测试环境时显式配置Storage模拟器参数,不要仅依赖hub自动发现:
testEnv = await initializeTestEnvironment({ projectId: "stst-et-interviewer-dev", hub: { host: "localhost", port: 4400, }, storage: { host: "localhost", port: 9199 } });
- 修正
assertFails的入参,不要传入无回调的then调用,直接传入上传任务本身即可:await assertFails(upload)。 - 先移除手动指定的存储桶参数,直接调用
rouge.storage()使用默认存储桶做测试,排除桶名不匹配的问题。
内容的提问来源于stack exchange,提问作者Frank Weslien
相关产品推荐
相关产品推荐

