Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rules-unit-testing, storage uploadBytes and uploadString never resolve #6530

Open
kris-kolve-jumio opened this issue Aug 13, 2022 · 7 comments

Comments

@kris-kolve-jumio
Copy link

kris-kolve-jumio commented Aug 13, 2022

[REQUIRED] Describe your environment

  • Operating System version: macOS v12.4
  • Browser version: Chrom 104.0.5112.79 (not relevant testing in vsCode with mocha)
  • Firebase SDK version: 9.9.2
  • Firebase tools version: 2.0.4
  • Mocha version:10.0.0
  • Firebase-tools version: 11.6.0
  • Firebase Product: rules-unit-testing; storage

[REQUIRED] Describe the problem

uploadBytes and uploadString storage functions do not resolve when using the @firebase/rules-unit-testing library. This causes the test to timeout even at 20+seconds.

This occurs with authenticatedContext, and unauthenticated contexts.

It is also worth noting that, getBytes, and getDownloadURL, resolve as expected.

I get the same results using Mocha and Jest. I have even tried the v8 SDK with the same results.

I did not have the upload issue when using the Admin SDK with the emulators. I have not tried it yet in my app but suspect it will work as desired.

Steps to reproduce:

  1. Create a firebase project and install Mocha and rules-unit-testing along with SDK
  2. TestEnvironment:
    a. host:"127.0.0.1"
    b. port:
  3. Create a testing folder and add a spec file for the test.
  4. in the test, create an unauthenticated context,
  5. attempt to upload using uploadBytes.

Result: upload promise will never resolve.

Expected Result: upload promise resolves well within a second and the test will fail (assuming the rules prevent uploads from unauthenticated users.

If I do not set the hub host to "127.0.0.1" I will get an ECCONREFUSED ::1:4400. Also interesting and possibly related:

  1. Setting the hub:{host:"127.0.0.1", port:4400} does not make storage discoverable and also does not throw an ECCONREFUSED ::1. However, it does make it so the getBytes test ends up throwing a "FirebaseError: Firebase Storage: Max retry time for operation exceeded"

  2. Setting the storage:{host:"127.0.0.1", port:9199} seems to make the storage discoverable. getBytes will resolve but uploadBytes will never resolve... I wonder if the host configuration is not propagating to post commands sent to the emulators? IDK.

Relevant Code:

storage.rules -

rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {

      function canRead(){
        return request.auth != null &&
          request.auth.token.email != null &&
          request.auth.token.email_verified ;
      }
      allow read: if canRead(); //ONLY ALLOW AUTHENTICATED USERS TO READ DATA
      allow write: if false; // Never allow writing
    }
  }
}

Note: it is necessary to add type:'module' in the package file so that imports work.

index.spec.js - TEST FILE

import * as fbTesting from "@firebase/rules-unit-testing"
import http from 'http'
import {readFileSync, createWriteStream} from 'fs'
// import firebase from 'firebase'
import assert from 'assert'
import { ref as storRef, deleteObject, uploadString, listAll, uploadBytes, getBytes } from "firebase/storage";
import 'mocha'

const {
  assertFails,
  assertSucceeds,
  initializeTestEnvironment,
} = fbTesting

const host = '127.0.0.1'
const port = 9199

/** 
   * @type {fbTesting.RulesTestEnvironment}
   */
let testEnv;

before(async ()=>{
  testEnv = await initializeTestEnvironment({
    projectId: 'demo-data-visualization',
    hub:{host, port:4400},
    storage:{ host,port:9199},
    
  })
  console.log("test Environment Created", testEnv.projectId, testEnv.emulators)
})

after("Cleaning up", async ()=>{
  await testEnv.cleanup()
})

describe("Testing the Storage Security Rules", function() {
  this.timeout(20000)

  describe("An Un-Authenticated user,", ()=>{

    /** @type {fbTesting.storage.Storage} */
    let alice
    before("Creating Unauth User",async ()=>{
      testEnv.clearStorage()
      const context = testEnv.unauthenticatedContext()
      alice = context.storage();
      // console.log(alice)
    })

    it("Can NOT read from storage", async ()=>{
      // console.log(alice)
      // THIS TEST WORKS
      const ref = storRef(alice, 'data.json')
      const blob = getBytes(ref)
      await assertFails(blob)
    })

    it("Can NOT upload a file", async ()=>{

      //THIS TEST TIMES OUT IN 20 seconds
      const ref = storRef(alice, 'data/test.json')
      const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21]);
      const upload = uploadBytes(ref, bytes, {contentType:'application/octet-stream'})
      await assertFails(upload)
    })
  })

})
@google-oss-bot
Copy link
Contributor

I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.

@kris-kolve-jumio
Copy link
Author

@jbalidiong I finished updating the ticket. Sorry for the delay.

@YehorPytomets
Copy link

Any updates on the issue?

@kris-kolve-jumio
Copy link
Author

@YehorPytomets I haven't heard anything but I have played around some more...

I tried moving to Jest. and interestingly with Jest 27.x I can get it to work as expected!

Jest 28/29.x I get the same issue. where uploads never resolve. So far I have been able to deduce that it has something to do with what libraries are injected when no browser is actually present. I think firebase Storage API is complicated by the fact that it relies on the GCP storage library. But this is out of my wheelhouse. Hopefully that makes some sense.

@YehorPytomets
Copy link

Thank you @kris-kolve-jumio.

I was able to play around with this and fixed the upload issue by changing the storage host from localhost to 127.0.0.1. I use Mocha ^10.2.0.

@daniel-sudz
Copy link

@kris-kolve-jumio I can confirm similar issue that jest 27 works but 28 and 29 do not. Also @YehorPytomets suggestion of switching to Mocha instead of jest did the trick for me. I could not use jest27 because I was using more bleeding-edge esm features that jest27 was too old for

@portrik
Copy link

portrik commented Apr 23, 2024

Hello, any updates on this? The issue is still relevant even with @firebase/rules-unit-testing@3.0.2. We have had to split our tests to ones without uploads running on vitest with the rest stuck on jest@27.5.1.

It seems like the uploadBytes function gets stuck after it opens a connection to the emulators. The logs do not contain any information about the upload until vitest stops the test after timeout. This is the only log that gets printed:

BadRequestError: request aborted
    at IncomingMessage.onAborted (/.../node_modules/raw-body/index.js:245:10)
    at IncomingMessage.emit (node:events:514:28)
    at IncomingMessage.emit (node:domain:489:12)
    at IncomingMessage._destroy (node:_http_incoming:224:10)
    at _destroy (node:internal/streams/destroy:109:10)
    at IncomingMessage.destroy (node:internal/streams/destroy:71:5)
    at abortIncoming (node:_http_server:782:9)
    at socketOnClose (node:_http_server:776:3)
    at Socket.emit (node:events:526:35)
    at Socket.emit (node:domain:489:12)

Running the upload with uploadBytesResumable returns only a single running snapshot that reports bytesTransfered as 0.

Security rules seem to have no effect - if I disable them all, the result of the upload is still the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants