@@ -220,6 +220,11 @@ describe("import/export end to end", () => {
220220 const bPath = path . join ( dbExportPath , "namespace-b.json" ) ;
221221 const bData = JSON . parse ( fs . readFileSync ( bPath ) . toString ( ) ) ;
222222 expect ( bData ) . to . equal ( null ) ;
223+
224+ // Clean up the admin sdk instances to prevent "Firebase app named <name> already exists." errors in later tests
225+ await aApp . delete ( ) ;
226+ await bApp . delete ( ) ;
227+ await cApp . delete ( ) ;
223228 } ) ;
224229
225230 it ( "should be able to import/export auth data" , async function ( this ) {
@@ -326,6 +331,9 @@ describe("import/export end to end", () => {
326331 expect ( user2 . emailVerified ) . to . be . true ;
327332
328333 await importCLI . stop ( ) ;
334+
335+ // Clean up the admin sdk instance to prevent "Firebase app named <name> already exists." errors in later tests
336+ await adminApp . delete ( ) ;
329337 } finally {
330338 delete process . env . FIREBASE_AUTH_EMULATOR_HOST ;
331339 }
@@ -489,6 +497,9 @@ describe("import/export end to end", () => {
489497 expect ( user4 . emailVerified ) . to . be . true ;
490498
491499 await importCLI . stop ( ) ;
500+
501+ // Clean up the admin sdk instance to prevent "Firebase app named <name> already exists." errors in later tests
502+ await adminApp . delete ( ) ;
492503 } finally {
493504 delete process . env . FIREBASE_AUTH_EMULATOR_HOST ;
494505 }
@@ -576,10 +587,14 @@ describe("import/export end to end", () => {
576587 expect ( user . passwordHash ) . to . match ( / : p a s s w o r d = t e s t i n g $ / ) ;
577588
578589 await importCLI . stop ( ) ;
590+
591+ // Clean up the admin sdk instance to prevent "Firebase app named <name> already exists." errors in later tests
592+ await adminApp . delete ( ) ;
579593 } finally {
580594 delete process . env . FIREBASE_AUTH_EMULATOR_HOST ;
581595 }
582596 } ) ;
597+
583598 it ( "should be able to export / import auth data with no users" , async function ( this ) {
584599 this . timeout ( 2 * TEST_SETUP_TIMEOUT ) ;
585600 await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
@@ -738,5 +753,278 @@ describe("import/export end to end", () => {
738753 // expect(buf.toString()).to.eql("a/b hello, world!");
739754
740755 await importCLI . stop ( ) ;
756+
757+ // Clean up the admin sdk instances to prevent "Firebase app named <name> already exists." errors in later tests
758+ await aApp . delete ( ) ;
759+ await bApp . delete ( ) ;
760+ } ) ;
761+
762+ it ( "should export all data when `--only` flag isn't used `emulators:export`" , async function ( this ) {
763+ this . timeout ( 2 * TEST_SETUP_TIMEOUT ) ;
764+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
765+
766+ // Start up emulator suite
767+ const emulatorsCLI = new CLIProcess ( "1" , __dirname ) ;
768+ await emulatorsCLI . start (
769+ "emulators:start" ,
770+ FIREBASE_PROJECT ,
771+ [ "--only" , "storage,auth" ] ,
772+ logIncludes ( ALL_EMULATORS_STARTED_LOG ) ,
773+ ) ;
774+
775+ const credPath = path . join ( __dirname , "service-account-key.json" ) ;
776+ const credential = fs . existsSync ( credPath )
777+ ? admin . credential . cert ( credPath )
778+ : admin . credential . applicationDefault ( ) ;
779+
780+ const config = readConfig ( ) ;
781+ const storagePort = config . emulators ! . storage . port ;
782+ process . env . STORAGE_EMULATOR_HOST = `http://${ await localhost ( ) } :${ storagePort } ` ;
783+
784+ // Write some data to export
785+ const aApp = admin . initializeApp (
786+ {
787+ projectId : FIREBASE_PROJECT ,
788+ storageBucket : "bucket-a" ,
789+ credential,
790+ } ,
791+ "storage-export-a" ,
792+ ) ;
793+ const bApp = admin . initializeApp (
794+ {
795+ projectId : FIREBASE_PROJECT ,
796+ storageBucket : "bucket-b" ,
797+ credential,
798+ } ,
799+ "storage-export-b" ,
800+ ) ;
801+
802+ // Write data to two buckets
803+ await aApp . storage ( ) . bucket ( ) . file ( "a/b.txt" ) . save ( "a/b hello, world!" ) ;
804+ await aApp . storage ( ) . bucket ( ) . file ( "c/d.txt" ) . save ( "c/d hello, world!" ) ;
805+ await bApp . storage ( ) . bucket ( ) . file ( "e/f.txt" ) . save ( "e/f hello, world!" ) ;
806+ await bApp . storage ( ) . bucket ( ) . file ( "g/h.txt" ) . save ( "g/h hello, world!" ) ;
807+
808+ // Create some accounts to export:
809+ const authPort = config . emulators ! . auth . port ;
810+ process . env . FIREBASE_AUTH_EMULATOR_HOST = `${ await localhost ( ) } :${ authPort } ` ;
811+ const cApp = admin . initializeApp (
812+ {
813+ projectId : FIREBASE_PROJECT ,
814+ credential : ADMIN_CREDENTIAL ,
815+ } ,
816+ "auth-export" ,
817+ ) ;
818+ await cApp . auth ( ) . createUser ( { uid : "123" , email : "foo@example.com" , password : "testing" } ) ;
819+ await cApp . auth ( ) . createUser ( { uid : "456" , email : "bar@example.com" , emailVerified : true } ) ;
820+
821+ // Ask for export
822+ const exportCLI = new CLIProcess ( "2" , __dirname ) ;
823+ const exportPath = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "emulator-data" ) ) ;
824+ await exportCLI . start (
825+ "emulators:export" ,
826+ FIREBASE_PROJECT ,
827+ [ exportPath ] ,
828+ logIncludes ( "Export complete" ) ,
829+ ) ;
830+ await exportCLI . stop ( ) ;
831+
832+ // Check that the right export files are created
833+ const storageExportPath = path . join ( exportPath , "storage_export" ) ;
834+ const storageExportFiles = fs . readdirSync ( storageExportPath ) . sort ( ) ;
835+ expect ( storageExportFiles ) . to . eql ( [ "blobs" , "buckets.json" , "metadata" ] ) ;
836+
837+ // Stop the suite
838+ await emulatorsCLI . stop ( ) ;
839+
840+ // Attempt to import
841+ const importCLI = new CLIProcess ( "3" , __dirname ) ;
842+ await importCLI . start (
843+ "emulators:start" ,
844+ FIREBASE_PROJECT ,
845+ [ "--only" , "storage,auth" , "--import" , exportPath ] ,
846+ logIncludes ( ALL_EMULATORS_STARTED_LOG ) ,
847+ ) ;
848+
849+ // List the files
850+ const [ aFiles ] = await aApp . storage ( ) . bucket ( ) . getFiles ( {
851+ prefix : "a/" ,
852+ } ) ;
853+ const aFileNames = aFiles . map ( ( f ) => f . name ) . sort ( ) ;
854+ expect ( aFileNames ) . to . eql ( [ "a/b.txt" ] ) ;
855+
856+ const [ bFiles ] = await bApp . storage ( ) . bucket ( ) . getFiles ( {
857+ prefix : "e/" ,
858+ } ) ;
859+ const bFileNames = bFiles . map ( ( f ) => f . name ) . sort ( ) ;
860+ expect ( bFileNames ) . to . eql ( [ "e/f.txt" ] ) ;
861+
862+ const user1 = await cApp . auth ( ) . getUserByEmail ( "foo@example.com" ) ;
863+ expect ( user1 . passwordHash ) . to . match ( / : p a s s w o r d = t e s t i n g $ / ) ;
864+ const user2 = await cApp . auth ( ) . getUserByEmail ( "bar@example.com" ) ;
865+ expect ( user2 . emailVerified ) . to . be . true ;
866+
867+ await importCLI . stop ( ) ;
868+
869+ // Clean up the admin sdk instances to prevent "Firebase app named <name> already exists." errors in later tests
870+ await aApp . delete ( ) ;
871+ await bApp . delete ( ) ;
872+ await cApp . delete ( ) ;
873+ } ) ;
874+
875+ it ( "should export only storage data with `emulators:export --only storage`" , async function ( this ) {
876+ this . timeout ( 2 * TEST_SETUP_TIMEOUT ) ;
877+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
878+
879+ // Start up emulator suite
880+ const emulatorsCLI = new CLIProcess ( "1" , __dirname ) ;
881+ await emulatorsCLI . start (
882+ "emulators:start" ,
883+ FIREBASE_PROJECT ,
884+ [ "--only" , "storage,auth" ] ,
885+ logIncludes ( ALL_EMULATORS_STARTED_LOG ) ,
886+ ) ;
887+
888+ const credPath = path . join ( __dirname , "service-account-key.json" ) ;
889+ const credential = fs . existsSync ( credPath )
890+ ? admin . credential . cert ( credPath )
891+ : admin . credential . applicationDefault ( ) ;
892+
893+ const config = readConfig ( ) ;
894+ const storagePort = config . emulators ! . storage . port ;
895+ process . env . STORAGE_EMULATOR_HOST = `http://${ await localhost ( ) } :${ storagePort } ` ;
896+
897+ // Write some data to export
898+ const aApp = admin . initializeApp (
899+ {
900+ projectId : FIREBASE_PROJECT ,
901+ storageBucket : "bucket-a" ,
902+ credential,
903+ } ,
904+ "storage-export-a" ,
905+ ) ;
906+ const bApp = admin . initializeApp (
907+ {
908+ projectId : FIREBASE_PROJECT ,
909+ storageBucket : "bucket-b" ,
910+ credential,
911+ } ,
912+ "storage-export-b" ,
913+ ) ;
914+
915+ // Write data to two buckets
916+ await aApp . storage ( ) . bucket ( ) . file ( "a/b.txt" ) . save ( "a/b hello, world!" ) ;
917+ await aApp . storage ( ) . bucket ( ) . file ( "c/d.txt" ) . save ( "c/d hello, world!" ) ;
918+ await bApp . storage ( ) . bucket ( ) . file ( "e/f.txt" ) . save ( "e/f hello, world!" ) ;
919+ await bApp . storage ( ) . bucket ( ) . file ( "g/h.txt" ) . save ( "g/h hello, world!" ) ;
920+
921+ // Create some accounts to export:
922+ const authPort = config . emulators ! . auth . port ;
923+ process . env . FIREBASE_AUTH_EMULATOR_HOST = `${ await localhost ( ) } :${ authPort } ` ;
924+ const cApp = admin . initializeApp (
925+ {
926+ projectId : FIREBASE_PROJECT ,
927+ credential : ADMIN_CREDENTIAL ,
928+ } ,
929+ "auth-export" ,
930+ ) ;
931+ await cApp . auth ( ) . createUser ( { uid : "123" , email : "foo@example.com" , password : "testing" } ) ;
932+ await cApp . auth ( ) . createUser ( { uid : "456" , email : "bar@example.com" , emailVerified : true } ) ;
933+
934+ // Ask for export
935+ const exportCLI = new CLIProcess ( "2" , __dirname ) ;
936+ const exportPath = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "emulator-data" ) ) ;
937+ await exportCLI . start (
938+ "emulators:export" ,
939+ FIREBASE_PROJECT ,
940+ [ exportPath , "--only" , "storage" ] ,
941+ logIncludes ( "Export complete" ) ,
942+ ) ;
943+ await exportCLI . stop ( ) ;
944+
945+ // Check that the right export files are created
946+ const storageExportPath = path . join ( exportPath , "storage_export" ) ;
947+ const storageExportFiles = fs . readdirSync ( storageExportPath ) . sort ( ) ;
948+ expect ( storageExportFiles ) . to . eql ( [ "blobs" , "buckets.json" , "metadata" ] ) ;
949+
950+ // Stop the suite
951+ await emulatorsCLI . stop ( ) ;
952+
953+ // Attempt to import
954+ const importCLI = new CLIProcess ( "3" , __dirname ) ;
955+ await importCLI . start (
956+ "emulators:start" ,
957+ FIREBASE_PROJECT ,
958+ [ "--only" , "storage,auth" , "--import" , exportPath ] ,
959+ logIncludes ( ALL_EMULATORS_STARTED_LOG ) ,
960+ ) ;
961+
962+ // List the files
963+ const [ aFiles ] = await aApp . storage ( ) . bucket ( ) . getFiles ( {
964+ prefix : "a/" ,
965+ } ) ;
966+ const aFileNames = aFiles . map ( ( f ) => f . name ) . sort ( ) ;
967+ expect ( aFileNames ) . to . eql ( [ "a/b.txt" ] ) ;
968+
969+ const [ bFiles ] = await bApp . storage ( ) . bucket ( ) . getFiles ( {
970+ prefix : "e/" ,
971+ } ) ;
972+ const bFileNames = bFiles . map ( ( f ) => f . name ) . sort ( ) ;
973+ expect ( bFileNames ) . to . eql ( [ "e/f.txt" ] ) ;
974+
975+ await expect ( cApp . auth ( ) . getUserByEmail ( "foo@example.com" ) )
976+ . to . eventually . be . rejectedWith ( Error )
977+ . and . have . property ( "code" , "auth/user-not-found" ) ;
978+ await expect ( cApp . auth ( ) . getUserByEmail ( "bar@example.com" ) )
979+ . to . eventually . be . rejectedWith ( Error )
980+ . and . have . property ( "code" , "auth/user-not-found" ) ;
981+
982+ await importCLI . stop ( ) ;
983+
984+ // Clean up the admin sdk instances to prevent "Firebase app named <name> already exists." errors in later tests
985+ await aApp . delete ( ) ;
986+ await bApp . delete ( ) ;
987+ await cApp . delete ( ) ;
988+ } ) ;
989+
990+ it ( "should be able to export using POST" , async function ( this ) {
991+ this . timeout ( 2 * TEST_SETUP_TIMEOUT ) ;
992+ await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
993+
994+ // Start up emulator suite
995+ const emulatorsCLI = new CLIProcess ( "1" , __dirname ) ;
996+ await emulatorsCLI . start (
997+ "emulators:start" ,
998+ FIREBASE_PROJECT ,
999+ [ "--only" , "firestore" ] ,
1000+ logIncludes ( ALL_EMULATORS_STARTED_LOG ) ,
1001+ ) ;
1002+
1003+ const config = readConfig ( ) ;
1004+ const hubPort = config . emulators ! . hub ! . port ;
1005+ const host = await localhost ( ) ;
1006+
1007+ // Ask for export using HTTP POST to hub
1008+ const exportPath = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "emulator-data" ) ) ;
1009+ const postData = JSON . stringify ( { path : exportPath } ) ;
1010+
1011+ const response = await fetch ( `http://${ host } :${ hubPort } /_admin/export` , {
1012+ method : "POST" ,
1013+ headers : {
1014+ "Content-Type" : "application/json" ,
1015+ } ,
1016+ body : postData ,
1017+ } ) ;
1018+
1019+ if ( ! response . ok ) {
1020+ throw new Error ( `Failed to export: ${ response . status } ` ) ;
1021+ }
1022+
1023+ // Check that the export files are created
1024+ const exportFiles = fs . readdirSync ( exportPath ) ;
1025+ expect ( exportFiles ) . to . include ( "firebase-export-metadata.json" ) ;
1026+
1027+ // Stop the suite
1028+ await emulatorsCLI . stop ( ) ;
7411029 } ) ;
7421030} ) ;
0 commit comments