1
1
import { PubSub } from '@google-cloud/pubsub' ;
2
2
import { Request , Response } from 'express' ;
3
+ import fetch from 'node-fetch' ;
3
4
import * as admin from 'firebase-admin' ;
4
5
import * as functions from 'firebase-functions' ;
5
6
import * as fs from 'fs' ;
6
- import * as https from 'https' ;
7
7
8
- export * from './pubsub-tests' ;
9
- export * from './database-tests' ;
10
- export * from './auth-tests' ;
11
- export * from './firestore-tests' ;
12
- export * from './https-tests' ;
13
- export * from './remoteConfig-tests' ;
14
- export * from './storage-tests' ;
15
- export * from './testLab-tests' ;
16
- const numTests = Object . keys ( exports ) . length ; // Assumption: every exported function is its own test.
8
+ import * as v1 from './v1/index' ;
9
+ const numTests = Object . keys ( v1 ) . filter ( ( k ) =>
10
+ ( { } . hasOwnProperty . call ( v1 [ k ] , '__endpoint' ) )
11
+ ) . length ;
12
+ export { v1 } ;
17
13
18
- import * as utils from './test-utils' ;
19
- import * as testLab from './testLab-utils' ;
14
+ import * as testLab from './v1/testLab-utils' ;
20
15
21
- import 'firebase-functions' ; // temporary shim until process.env.FIREBASE_CONFIG available natively in GCF(BUG 63586213)
22
- import { config } from 'firebase-functions' ;
23
16
const firebaseConfig = JSON . parse ( process . env . FIREBASE_CONFIG ) ;
24
- admin . initializeApp ( ) ;
25
17
const REGION = functions . config ( ) . functions . test_region ;
18
+ admin . initializeApp ( ) ;
26
19
27
- // TODO(klimt): Get rid of this once the JS client SDK supports callable triggers.
28
- function callHttpsTrigger ( name : string , data : any , baseUrl ) {
29
- return utils . makeRequest (
20
+ function callHttpsTrigger ( name : string , data : any ) {
21
+ return fetch (
22
+ `https:// ${ REGION } - ${ firebaseConfig . projectId } .cloudfunctions.net/ ${ name } ` ,
30
23
{
31
24
method : 'POST' ,
32
- host : REGION + '-' + firebaseConfig . projectId + '.' + baseUrl ,
33
- path : '/' + name ,
34
25
headers : {
35
26
'Content-Type' : 'application/json' ,
36
27
} ,
37
- } ,
38
- JSON . stringify ( { data } )
28
+ body : JSON . stringify ( { data } ) ,
29
+ }
39
30
) ;
40
31
}
41
32
42
33
async function callScheduleTrigger ( functionName : string , region : string ) {
43
34
const accessToken = await admin . credential
44
35
. applicationDefault ( )
45
36
. getAccessToken ( ) ;
46
- return new Promise < string > ( ( resolve , reject ) => {
47
- const request = https . request (
48
- {
49
- method : 'POST' ,
50
- host : 'cloudscheduler.googleapis.com' ,
51
- path : `/v1/projects/${ firebaseConfig . projectId } /locations/us-central1/jobs/firebase-schedule-${ functionName } -${ region } :run` ,
52
- headers : {
53
- 'Content-Type' : 'application/json' ,
54
- Authorization : `Bearer ${ accessToken . access_token } ` ,
55
- } ,
37
+ const response = await fetch (
38
+ `https://cloudscheduler.googleapis.com/v1/projects/${ firebaseConfig . projectId } /locations/us-central1/jobs/firebase-schedule-${ functionName } -${ region } :run` ,
39
+ {
40
+ method : 'POST' ,
41
+ headers : {
42
+ 'Content-Type' : 'application/json' ,
43
+ Authorization : `Bearer ${ accessToken . access_token } ` ,
56
44
} ,
57
- ( response ) => {
58
- if ( response . statusCode ! / 100 != 2 ) {
59
- reject (
60
- new Error ( 'Failed request with status ' + response . statusCode ! )
61
- ) ;
62
- return ;
63
- }
64
- let body = '' ;
65
- response . on ( 'data' , ( chunk ) => {
66
- body += chunk ;
67
- } ) ;
68
- response . on ( 'end' , ( ) => {
69
- console . log ( `Successfully scheduled function ${ functionName } ` ) ;
70
- resolve ( body ) ;
71
- } ) ;
72
- }
73
- ) ;
74
- request . on ( 'error' , ( err ) => {
75
- console . error ( 'Failed to schedule cloud scheduler job with error' , err ) ;
76
- reject ( err ) ;
77
- } ) ;
78
- request . write ( '{}' ) ;
79
- request . end ( ) ;
80
- } ) ;
45
+ }
46
+ ) ;
47
+ if ( ! response . ok ) {
48
+ throw new Error ( `Failed request with status ${ response . status } !` ) ;
49
+ }
50
+ const data = await response . text ( ) ;
51
+ functions . logger . log ( `Successfully scheduled function ${ functionName } ` , data ) ;
52
+ return ;
53
+ }
54
+
55
+ async function updateRemoteConfig (
56
+ testId : string ,
57
+ accessToken : string
58
+ ) : Promise < void > {
59
+ await fetch (
60
+ `https://firebaseremoteconfig.googleapis.com/v1/projects/${ firebaseConfig . projectId } /remoteConfig` ,
61
+ {
62
+ method : 'PUT' ,
63
+ headers : {
64
+ Authorization : `Bearer ${ accessToken } ` ,
65
+ 'Content-Type' : 'application/json; UTF-8' ,
66
+ 'Accept-Encoding' : 'gzip' ,
67
+ 'If-Match' : '*' ,
68
+ } ,
69
+ body : JSON . stringify ( { version : { description : testId } } ) ,
70
+ }
71
+ ) ;
72
+ }
73
+
74
+ function v1Tests ( testId : string , accessToken : string ) {
75
+ return [
76
+ // A database write to trigger the Firebase Realtime Database tests.
77
+ admin
78
+ . database ( )
79
+ . ref ( `dbTests/${ testId } /start` )
80
+ . set ( { '.sv' : 'timestamp' } ) ,
81
+ // A Pub/Sub publish to trigger the Cloud Pub/Sub tests.
82
+ new PubSub ( )
83
+ . topic ( 'pubsubTests' )
84
+ . publish ( Buffer . from ( JSON . stringify ( { testId } ) ) ) ,
85
+ // A user creation to trigger the Firebase Auth user creation tests.
86
+ admin
87
+ . auth ( )
88
+ . createUser ( {
89
+ email : `${ testId } @fake.com` ,
90
+ password : 'secret' ,
91
+ displayName : `${ testId } ` ,
92
+ } )
93
+ . then ( ( userRecord ) => {
94
+ // A user deletion to trigger the Firebase Auth user deletion tests.
95
+ admin . auth ( ) . deleteUser ( userRecord . uid ) ;
96
+ } ) ,
97
+ // A firestore write to trigger the Cloud Firestore tests.
98
+ admin
99
+ . firestore ( )
100
+ . collection ( 'tests' )
101
+ . doc ( testId )
102
+ . set ( { test : testId } ) ,
103
+ // Invoke a callable HTTPS trigger.
104
+ callHttpsTrigger ( 'v1-callableTests' , { foo : 'bar' , testId } ) ,
105
+ // A Remote Config update to trigger the Remote Config tests.
106
+ updateRemoteConfig ( testId , accessToken ) ,
107
+ // A storage upload to trigger the Storage tests
108
+ admin
109
+ . storage ( )
110
+ . bucket ( )
111
+ . upload ( '/tmp/' + testId + '.txt' ) ,
112
+ testLab . startTestRun ( firebaseConfig . projectId , testId ) ,
113
+ // Invoke the schedule for our scheduled function to fire
114
+ callScheduleTrigger ( 'v1-schedule' , 'us-central1' ) ,
115
+ ] ;
81
116
}
82
117
83
118
export const integrationTests : any = functions
@@ -86,12 +121,6 @@ export const integrationTests: any = functions
86
121
timeoutSeconds : 540 ,
87
122
} )
88
123
. https . onRequest ( async ( req : Request , resp : Response ) => {
89
- // We take the base url for our https call (cloudfunctions.net, txckloud.net, etc) from the request
90
- // so that it changes with the environment that the tests are run in
91
- const baseUrl = req . hostname
92
- . split ( '.' )
93
- . slice ( 1 )
94
- . join ( '.' ) ;
95
124
const testId = admin
96
125
. database ( )
97
126
. ref ( )
@@ -101,71 +130,15 @@ export const integrationTests: any = functions
101
130
. ref ( `testRuns/${ testId } /timestamp` )
102
131
. set ( Date . now ( ) ) ;
103
132
const testIdRef = admin . database ( ) . ref ( `testRuns/${ testId } ` ) ;
104
- console . log ( 'testId is: ' , testId ) ;
133
+ functions . logger . info ( 'testId is: ' , testId ) ;
105
134
fs . writeFile ( '/tmp/' + testId + '.txt' , 'test' , ( ) => { } ) ;
106
135
try {
107
- await Promise . all ( [
108
- // A database write to trigger the Firebase Realtime Database tests.
109
- admin
110
- . database ( )
111
- . ref ( `dbTests/${ testId } /start` )
112
- . set ( { '.sv' : 'timestamp' } ) ,
113
- // A Pub/Sub publish to trigger the Cloud Pub/Sub tests.
114
- new PubSub ( )
115
- . topic ( 'pubsubTests' )
116
- . publish ( Buffer . from ( JSON . stringify ( { testId } ) ) ) ,
117
- // A user creation to trigger the Firebase Auth user creation tests.
118
- admin
119
- . auth ( )
120
- . createUser ( {
121
- email : `${ testId } @fake.com` ,
122
- password : 'secret' ,
123
- displayName : `${ testId } ` ,
124
- } )
125
- . then ( ( userRecord ) => {
126
- // A user deletion to trigger the Firebase Auth user deletion tests.
127
- admin . auth ( ) . deleteUser ( userRecord . uid ) ;
128
- } ) ,
129
- // A firestore write to trigger the Cloud Firestore tests.
130
- admin
131
- . firestore ( )
132
- . collection ( 'tests' )
133
- . doc ( testId )
134
- . set ( { test : testId } ) ,
135
- // Invoke a callable HTTPS trigger.
136
- callHttpsTrigger ( 'callableTests' , { foo : 'bar' , testId } , baseUrl ) ,
137
- // A Remote Config update to trigger the Remote Config tests.
138
- admin . credential
139
- . applicationDefault ( )
140
- . getAccessToken ( )
141
- . then ( ( accessToken ) => {
142
- const options = {
143
- hostname : 'firebaseremoteconfig.googleapis.com' ,
144
- path : `/v1/projects/${ firebaseConfig . projectId } /remoteConfig` ,
145
- method : 'PUT' ,
146
- headers : {
147
- Authorization : 'Bearer ' + accessToken . access_token ,
148
- 'Content-Type' : 'application/json; UTF-8' ,
149
- 'Accept-Encoding' : 'gzip' ,
150
- 'If-Match' : '*' ,
151
- } ,
152
- } ;
153
- const request = https . request ( options , ( resp ) => { } ) ;
154
- request . write ( JSON . stringify ( { version : { description : testId } } ) ) ;
155
- request . end ( ) ;
156
- } ) ,
157
- // A storage upload to trigger the Storage tests
158
- admin
159
- . storage ( )
160
- . bucket ( )
161
- . upload ( '/tmp/' + testId + '.txt' ) ,
162
- testLab . startTestRun ( firebaseConfig . projectId , testId ) ,
163
- // Invoke the schedule for our scheduled function to fire
164
- callScheduleTrigger ( 'schedule' , 'us-central1' ) ,
165
- ] ) ;
166
-
136
+ const accessToken = await admin . credential
137
+ . applicationDefault ( )
138
+ . getAccessToken ( ) ;
139
+ await Promise . all ( [ ...v1Tests ( testId , accessToken . access_token ) ] ) ;
167
140
// On test completion, check that all tests pass and reply "PASS", or provide further details.
168
- console . log ( 'Waiting for all tests to report they pass...' ) ;
141
+ functions . logger . info ( 'Waiting for all tests to report they pass...' ) ;
169
142
await new Promise < void > ( ( resolve , reject ) => {
170
143
setTimeout ( ( ) => reject ( new Error ( 'Timeout' ) ) , 5 * 60 * 1000 ) ;
171
144
let testsExecuted = 0 ;
@@ -179,7 +152,7 @@ export const integrationTests: any = functions
179
152
) ;
180
153
return ;
181
154
}
182
- console . log (
155
+ functions . logger . info (
183
156
`${ snapshot . key } passed (${ testsExecuted } of ${ numTests } )`
184
157
) ;
185
158
if ( testsExecuted < numTests ) {
@@ -190,10 +163,10 @@ export const integrationTests: any = functions
190
163
resolve ( ) ;
191
164
} ) ;
192
165
} ) ;
193
- console . log ( 'All tests pass!' ) ;
166
+ functions . logger . info ( 'All tests pass!' ) ;
194
167
resp . status ( 200 ) . send ( 'PASS \n' ) ;
195
168
} catch ( err ) {
196
- console . log ( `Some tests failed: ${ err } ` ) ;
169
+ functions . logger . info ( `Some tests failed: ${ err } ` , err ) ;
197
170
resp
198
171
. status ( 500 )
199
172
. send (
0 commit comments