Reputation: 1
When running the below test via npm test
, I get the following output:
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/[myname]/development/Construct/construct/infra/test/storage.test.js)
at listOnTimeout (node:internal/timers:564:17)
at process.processTimers (node:internal/timers:507:7)
I have the storage emulator running, with output telling me it is running on 127.0.0.1:9199
You'll notice I have a debug
statement in storage.rules
. This is consistently outputting a result, so the rules do appear to be getting run.
I can't find definitive documentation on what env var I should be setting, but it seems like process.env.STORAGE_EMULATOR_HOST
is probably the right one?
My colleague, running the same test on his machine, is finding it runs successfully. So it seems the problem is somehow connected to my local setup.
I also have tests that test firestore.rules
as well as storage.rules
(running against the Firestore emulator) and they run with no problem.
node -v
: v18.0.0
npm -v
: 8.6.0
firebase --version
: 13.32.0
macOS 15.3.1 (24D70)
I came across this while searching for a solution: https://github.com/firebase/firebase-tools/issues/4908 I appear to have the same problem.
Run the storage emulator, check the host and port match what's being set in STORAGE_EMULATOR_HOST
in storage.test.js
, then run the test in storage.test.js
via npm test
.
I expect to get feedback on whether my test has passed or failed, as I do for my firestore.rules
tests, eg "PERMISSION_DENIED" when I expect permission to be denied, and a green tick when tests pass.
storage.test.js:
import {
assertFails,
assertSucceeds,
initializeTestEnvironment,
} from "@firebase/rules-unit-testing";
import { ref, uploadBytes } from "firebase/storage";
import { readFileSync } from "fs";
import { afterEach, describe, it } from "mocha";
import { v4 as uuid_v4 } from "uuid";
process.env.STORAGE_EMULATOR_HOST = "127.0.0.1:9199";
//process.env.DATASTORE_EMULATOR_HOST = "127.0.0.1:9199";
const projectId = "app-construct-firebase-storage-security-rules-test";
const firebaseStorage = {
rules: readFileSync("./../storage.rules", "utf8"),
host: "127.0.0.1",
port: 9199,
};
const testEnv = await initializeTestEnvironment({
projectId: projectId,
storage: firebaseStorage,
});
describe("firebase storage security rules unit tests", () => {
const userID = uuid_v4();
const userEmail = "[email protected]";
const tokenOptions = {
email: userEmail,
};
function avatarPath(myUserID) {
return `avatars/${myUserID}`;
}
describe("********** `avatars` storage **********", () => {
it("users can only create avatars stored against their own user ID", async () => {
const storage = testEnv
.authenticatedContext(userID, tokenOptions)
.storage();
const image = readFileSync("./dummy-avatar.png");
const storagePath = avatarPath(userID);
const userImageRef = storage.ref(storagePath);
await assertSucceeds(
userImageRef.put(image, { contentType: "image/png" })
);
});
});
});
storage.rules:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read;
}
match /avatars/{userID} {
allow write, delete: if debug(request.auth.uid) == debug(userID);
}
}
}
firebase.json:
{
"hosting": {
"public": "app/build/web",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
},
"firestore": {
"rules": "infra/firestore.rules"
},
"storage": {
"rules": "infra/storage.rules"
},
"emulators": {
"auth": {
"port": 9099
},
"firestore": {
"port": 8080
},
"storage": {
"port": 9199
},
"ui": {
"enabled": true
},
"singleProjectMode": false
},
"functions": [
{
"source": "functions",
"codebase": "default",
"ignore": ["venv", ".git", "firebase-debug.log", "firebase-debug.*.log"]
}
]
}
package.json:
{
"name": "security-rules-tests",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"test": "mocha \"./storage.test.js\" --exit"
},
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@firebase/rules-unit-testing": "^3.0.4",
"firebase-admin": "^12.7.0",
"firebase-tools": "^13.20.2",
"mocha": "^10.7.3",
"uuid": "^11.0.3"
}
}
npm test --debug
gives the same output.
But if I check firestore-debug.log
, I see this:
Mar 03, 2025 8:52:05 PM io.gapi.emulators.netty.HttpVersionRoutingHandler channelRead INFO: Detected non-HTTP/2 connection.
...and if I check firebase-debug.log
, I see this:
[debug] [2025-03-03T20:55:36.909Z] >>> [apiv2][query] POST http://127.0.0.1:5001/functions/projects/app-construct-dv-428210/trigger_multicast [none] [debug] [2025-03-03T20:55:36.909Z] >>> [apiv2][body] POST http://127.0.0.1:5001/functions/projects/app-construct-dv-428210/trigger_multicast {"specversion":"1.0","id":"25e21402-0f42-4bf9-9197-7414f86c2eae","type":"google.cloud.storage.object.v1.finalized","source":"//storage.googleapis.com/projects/_/buckets/app-construct-firebase-storage-security-rules-test/objects/avatars/37d5ca50-211d-4d0c-993c-771c5668e0f8","time":"2025-03-03T20:55:36.888Z","data":{"kind":"storage#object","name":"avatars/37d5ca50-211d-4d0c-993c-771c5668e0f8","bucket":"app-construct-firebase-storage-security-rules-test","generation":"1741035336888","metageneration":"1","contentType":"image/png","timeCreated":"2025-03-03T20:55:36.888Z","updated":"2025-03-03T20:55:36.888Z","storageClass":"STANDARD","size":"561","md5Hash":"nWSYBL5eqqiZUZaMdncOqg==","etag":"3ZOhitoec/6YbVv42oz62nkv+BY","metadata":{"firebaseStorageDownloadTokens":"f5e7cfc7-8d49-43c5-b642-5345153f2748"},"crc32c":"16/Irg==","timeStorageClassUpdated":"2025-03-03T20:55:36.888Z","id":"app-construct-firebase-storage-security-rules-test/avatars/37d5ca50-211d-4d0c-993c-771c5668e0f8/1741035336888","selfLink":"http://127.0.0.1:9199/storage/v1/b/app-construct-firebase-storage-security-rules-test/o/avatars%2F37d5ca50-211d-4d0c-993c-771c5668e0f8","mediaLink":"http://127.0.0.1:9199/download/storage/v1/b/app-construct-firebase-storage-security-rules-test/o/avatars%2F37d5ca50-211d-4d0c-993c-771c5668e0f8?generation=1741035336888&alt=media"}} [debug] [2025-03-03T20:55:36.914Z] <<< [apiv2][status] POST http://127.0.0.1:5001/functions/projects/app-construct-dv-428210/trigger_multicast 200 [debug] [2025-03-03T20:55:36.914Z] <<< [apiv2][body] POST http://127.0.0.1:5001/functions/projects/app-construct-dv-428210/trigger_multicast {"status":"multicast_acknowledged"}
...and if I check database-debug.log
, I see nothing recent.
(I'm not actually sure which of these files is the one relevant to storage rules?)
Upvotes: 0
Views: 13