Skip to content

Commit 48d56ed

Browse files
committed
Added test for fetch for firestore
1 parent 6d63073 commit 48d56ed

File tree

11 files changed

+129
-28
lines changed

11 files changed

+129
-28
lines changed

common/api-review/storage.api.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class _FirebaseStorageImpl implements FirebaseStorage {
5858
constructor(
5959
app: FirebaseApp, _authProvider: Provider<FirebaseAuthInternalName>,
6060
_appCheckProvider: Provider<AppCheckInternalComponentName>,
61-
_url?: string | undefined, _firebaseVersion?: string | undefined);
61+
_url?: string | undefined, _firebaseVersion?: string | undefined, _isUsingEmulator?: boolean);
6262
readonly app: FirebaseApp;
6363
// (undocumented)
6464
readonly _appCheckProvider: Provider<AppCheckInternalComponentName>;
@@ -77,6 +77,8 @@ export class _FirebaseStorageImpl implements FirebaseStorage {
7777
_getAuthToken(): Promise<string | null>;
7878
get host(): string;
7979
set host(host: string);
80+
// (undocumented)
81+
_isUsingEmulator: boolean;
8082
// Warning: (ae-forgotten-export) The symbol "ConnectionType" needs to be exported by the entry point index.d.ts
8183
// Warning: (ae-forgotten-export) The symbol "RequestInfo" needs to be exported by the entry point index.d.ts
8284
// Warning: (ae-forgotten-export) The symbol "Connection" needs to be exported by the entry point index.d.ts

packages/data-connect/test/unit/fetch.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
import { expect, use } from 'chai';
1919
import chaiAsPromised from 'chai-as-promised';
2020
import * as sinon from 'sinon';
21+
import sinonChai from 'sinon-chai';
2122

2223
import { dcFetch, initializeFetch } from '../../src/network/fetch';
2324
import { CallerSdkType, CallerSdkTypeEnum } from '../../src/network/transport';
2425
use(chaiAsPromised);
26+
use(sinonChai);
2527
function mockFetch(json: object, reject: boolean): sinon.SinonStub {
2628
const fakeFetchImpl = sinon.stub().returns(
2729
Promise.resolve({

packages/firestore/src/lite-api/components.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ export function makeDatabaseInfo(
110110
persistenceKey: string,
111111
settings: FirestoreSettingsImpl
112112
): DatabaseInfo {
113-
const privateSettings = settings as PrivateSettings;
114113
return new DatabaseInfo(
115114
databaseId,
116115
appId,
@@ -121,6 +120,6 @@ export function makeDatabaseInfo(
121120
settings.experimentalAutoDetectLongPolling,
122121
cloneLongPollingOptions(settings.experimentalLongPollingOptions),
123122
settings.useFetchStreams,
124-
privateSettings.emulatorOptions !== undefined
123+
settings.isUsingEmulator
125124
);
126125
}

packages/firestore/src/lite-api/settings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ export class FirestoreSettingsImpl {
112112
readonly useFetchStreams: boolean;
113113
readonly localCache?: FirestoreLocalCache;
114114

115+
readonly isUsingEmulator: boolean;
116+
115117
// Can be a google-auth-library or gapi client.
116118
// eslint-disable-next-line @typescript-eslint/no-explicit-any
117119
credentials?: any;
@@ -130,6 +132,7 @@ export class FirestoreSettingsImpl {
130132
this.host = settings.host;
131133
this.ssl = settings.ssl ?? DEFAULT_SSL;
132134
}
135+
this.isUsingEmulator = settings.emulatorOptions !== undefined;
133136

134137
this.credentials = settings.credentials;
135138
this.ignoreUndefinedProperties = !!settings.ignoreUndefinedProperties;

packages/firestore/src/platform/browser_lite/fetch_connection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class FetchConnection extends RestConnection {
5252
headers,
5353
body: requestJson
5454
};
55-
if (isCloudWorkstation(url) && isUsingEmulator) {
55+
if (isCloudWorkstation(new URL(url).host) && isUsingEmulator) {
5656
fetchArgs.credentials = 'include';
5757
}
5858
response = await fetch(url, fetchArgs);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as sinon from 'sinon';
19+
import { expect, use } from 'chai';
20+
import { DatabaseId } from '../../../src/core/database_info';
21+
import { makeDatabaseInfo } from '../../../src/lite-api/components';
22+
import {
23+
FirestoreSettingsImpl,
24+
PrivateSettings
25+
} from '../../../src/lite-api/settings';
26+
import { ResourcePath } from '../../../src/model/path';
27+
import { FetchConnection } from '../../../src/platform/browser_lite/fetch_connection';
28+
import sinonChai from 'sinon-chai';
29+
import chaiAsPromised from 'chai-as-promised';
30+
31+
use(sinonChai);
32+
use(chaiAsPromised);
33+
34+
describe('Fetch Connection', () => {
35+
it('should pass in credentials if using emulator and cloud workstation', async () => {
36+
const privateSettings: PrivateSettings = {
37+
emulatorOptions: {},
38+
host: 'abc.cloudworkstations.dev'
39+
};
40+
console.log(
41+
makeDatabaseInfo(
42+
DatabaseId.empty(),
43+
'',
44+
'',
45+
new FirestoreSettingsImpl(privateSettings)
46+
)
47+
);
48+
const stub = sinon.stub(globalThis, 'fetch');
49+
stub.resolves({
50+
ok: true,
51+
json() {
52+
return Promise.resolve();
53+
}
54+
} as Response);
55+
const fetchConnection = new FetchConnection(
56+
makeDatabaseInfo(
57+
DatabaseId.empty(),
58+
'',
59+
'',
60+
new FirestoreSettingsImpl({
61+
host: 'abc.cloudworkstations.dev',
62+
emulatorOptions: {}
63+
})
64+
)
65+
);
66+
await fetchConnection.invokeRPC(
67+
'Commit',
68+
new ResourcePath([]),
69+
{},
70+
null,
71+
null
72+
);
73+
expect(stub).to.have.been.calledWithMatch(
74+
'https://abc.cloudworkstations.dev/v1/:commit',
75+
{ credentials: 'include' }
76+
);
77+
stub.restore();
78+
});
79+
});

packages/storage/src/implementation/request.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,9 @@ class NetworkRequest<I extends ConnectionType, O> implements Request<O> {
115115
.send(
116116
this.url_,
117117
this.method_,
118+
this.isUsingEmulator,
118119
this.body_,
119-
this.headers_,
120-
this.isUsingEmulator
120+
this.headers_
121121
)
122122
.then(() => {
123123
if (this.progressCallback_ !== null) {

packages/storage/src/platform/browser/connection.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ abstract class XhrConnection<T extends ConnectionType>
6363
send(
6464
url: string,
6565
method: string,
66+
isUsingEmulator: boolean,
6667
body?: ArrayBufferView | Blob | string,
67-
headers?: Headers,
68-
isUsingEmulator?: boolean
68+
headers?: Headers
6969
): Promise<void> {
7070
if (this.sent_) {
7171
throw internalError('cannot .send() more than once');

packages/storage/test/browser/connection.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,27 @@ describe('Connections', () => {
2424
it('XhrConnection.send() should not reject on network errors', async () => {
2525
const fakeXHR = useFakeXMLHttpRequest();
2626
const connection = new XhrBytesConnection();
27-
const sendPromise = connection.send('testurl', 'GET');
27+
const sendPromise = connection.send('testurl', 'GET', false);
2828
// simulate a network error
2929
((connection as any).xhr_ as SinonFakeXMLHttpRequest).error();
3030
await sendPromise;
3131
expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR);
3232
fakeXHR.restore();
3333
});
34+
it('XhrConnection.send() should send credentials when using cloud workstation', async () => {
35+
const fakeXHR = useFakeXMLHttpRequest();
36+
const connection = new XhrBytesConnection();
37+
const sendPromise = connection.send(
38+
'https://abc.cloudworkstations.dev',
39+
'GET',
40+
true
41+
);
42+
// simulate a network error
43+
((connection as any).xhr_ as SinonFakeXMLHttpRequest).error();
44+
await sendPromise;
45+
expect(
46+
((connection as any).xhr_ as SinonFakeXMLHttpRequest).withCredentials
47+
).to.be.true;
48+
fakeXHR.restore();
49+
});
3450
});

packages/storage/test/node/connection.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,25 @@ describe('Connections', () => {
2727
const fetchStub = stub(globalThis, 'fetch').rejects();
2828
await connection.send('testurl', 'GET', false);
2929
expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR);
30+
31+
fetchStub.restore();
32+
});
33+
it('FetchConnection.send() should send credentials on cloud workstations', async () => {
34+
const connection = new FetchBytesConnection();
35+
36+
const fetchStub = stub(globalThis, 'fetch').rejects();
37+
await connection.send(
38+
'http://something.cloudworkstations.dev',
39+
'GET',
40+
true
41+
);
42+
expect(connection.getErrorCode()).to.equal(ErrorCode.NETWORK_ERROR);
43+
expect(fetchStub).to.have.been.calledWithMatch(
44+
'http://something.cloudworkstations.dev',
45+
{
46+
credentials: 'include'
47+
}
48+
);
3049
fetchStub.restore();
3150
});
3251
});

packages/storage/test/unit/service.test.ts

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -281,25 +281,6 @@ GOOG4-RSA-SHA256`
281281
expect(service._protocol).to.equal('https');
282282
void getDownloadURL(ref(service, 'test.png'));
283283
});
284-
it('sets the credentials', () => {
285-
const stub = sandbox.stub(globalThis, 'fetch').resolves();
286-
const textConnection = newTextConnection();
287-
textConnection.send(
288-
'http://something.cloudworkstations.dev',
289-
'POST',
290-
true,
291-
undefined,
292-
undefined
293-
);
294-
expect(stub).to.have.been.called;
295-
expect(stub).to.have.been.calledWithMatch(
296-
'http://something.cloudworkstations.dev',
297-
{
298-
credentials: 'include'
299-
}
300-
);
301-
stub.restore();
302-
});
303284
it('sets mock user token string if specified', done => {
304285
const mockUserToken = 'my-mock-user-token';
305286
function newSend(

0 commit comments

Comments
 (0)