mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
255 lines
7.1 KiB
TypeScript
255 lines
7.1 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import { expect, use } from 'chai';
|
|
import chaiAsPromised from 'chai-as-promised';
|
|
import * as sinon from 'sinon';
|
|
import sinonChai from 'sinon-chai';
|
|
|
|
import { CallerSdkType, CallerSdkTypeEnum } from '../../src/network';
|
|
import { dcFetch, initializeFetch } from '../../src/network/rest';
|
|
use(chaiAsPromised);
|
|
use(sinonChai);
|
|
function mockFetch(json: object, reject: boolean): sinon.SinonStub {
|
|
const fakeFetchImpl = sinon.stub().returns(
|
|
Promise.resolve({
|
|
json: () => {
|
|
return Promise.resolve(json);
|
|
},
|
|
status: reject ? 401 : 200
|
|
} as Response)
|
|
);
|
|
initializeFetch(fakeFetchImpl);
|
|
return fakeFetchImpl;
|
|
}
|
|
describe('fetch', () => {
|
|
it('should throw an error with just the message when the server responds with an error with a message property in the body', async () => {
|
|
const message = 'Failed to connect to Postgres instance';
|
|
mockFetch(
|
|
{
|
|
code: 401,
|
|
message,
|
|
extensions: {
|
|
dataConnect: []
|
|
}
|
|
},
|
|
true
|
|
);
|
|
await expect(
|
|
dcFetch(
|
|
'http://localhost',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
false,
|
|
CallerSdkTypeEnum.Base,
|
|
false
|
|
)
|
|
).to.eventually.be.rejectedWith(message);
|
|
});
|
|
it('should throw a stringified message when the server responds with an error without a message property in the body', async () => {
|
|
const message = 'Failed to connect to Postgres instance';
|
|
const json = {
|
|
code: 401,
|
|
message1: message
|
|
};
|
|
mockFetch(json, true);
|
|
await expect(
|
|
dcFetch(
|
|
'http://localhost',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
false,
|
|
CallerSdkTypeEnum.Base,
|
|
false
|
|
)
|
|
).to.eventually.be.rejectedWith(JSON.stringify(json));
|
|
});
|
|
it('should throw a stringified message when the server responds with an error without a message property in the body', async () => {
|
|
const json = {
|
|
'data': { 'abc': 'def' },
|
|
'errors': [
|
|
{
|
|
'message':
|
|
'SQL query error: pq: duplicate key value violates unique constraint movie_pkey',
|
|
'locations': [],
|
|
'path': ['the_matrix'],
|
|
'extensions': null
|
|
}
|
|
]
|
|
};
|
|
mockFetch(json, false);
|
|
await expect(
|
|
dcFetch(
|
|
'http://localhost',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
false,
|
|
CallerSdkTypeEnum.Base,
|
|
false
|
|
)
|
|
).to.eventually.be.rejected.then(error => {
|
|
expect(error.response.data).to.eq(json.data);
|
|
expect(error.response.errors).to.eq(json.errors);
|
|
});
|
|
});
|
|
it('should assign different values to custom headers based on the _callerSdkType argument (_isUsingGen is false)', async () => {
|
|
const json = {
|
|
code: 200,
|
|
message1: 'success'
|
|
};
|
|
const fakeFetchImpl = mockFetch(json, false);
|
|
|
|
for (const callerSdkType in CallerSdkTypeEnum) {
|
|
// this check is done to follow the best practices outlined by the "guard-for-in" eslint rule
|
|
if (
|
|
Object.prototype.hasOwnProperty.call(CallerSdkTypeEnum, callerSdkType)
|
|
) {
|
|
await dcFetch(
|
|
'http://localhost',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
false, // _isUsingGen is false
|
|
callerSdkType as CallerSdkType,
|
|
false
|
|
);
|
|
|
|
let expectedHeaderRegex: RegExp;
|
|
if (callerSdkType === CallerSdkTypeEnum.Base) {
|
|
// should not contain any "js/xxx" substring
|
|
expectedHeaderRegex = RegExp(/^((?!js\/\w).)*$/);
|
|
} else if (callerSdkType === CallerSdkTypeEnum.Generated) {
|
|
expectedHeaderRegex = RegExp(/js\/gen/);
|
|
} else {
|
|
expectedHeaderRegex = RegExp(`js\/${callerSdkType.toLowerCase()}`);
|
|
}
|
|
expect(
|
|
fakeFetchImpl.calledWithMatch(
|
|
'http://localhost',
|
|
sinon.match.hasNested(
|
|
'headers.X-Goog-Api-Client',
|
|
sinon.match(expectedHeaderRegex)
|
|
)
|
|
)
|
|
).to.be.true;
|
|
}
|
|
}
|
|
});
|
|
it('should assign custom headers based on _callerSdkType before checking to-be-deprecated _isUsingGen', async () => {
|
|
const json = {
|
|
code: 200,
|
|
message1: 'success'
|
|
};
|
|
const fakeFetchImpl = mockFetch(json, false);
|
|
|
|
for (const callerSdkType in CallerSdkTypeEnum) {
|
|
// this check is done to follow the best practices outlined by the "guard-for-in" eslint rule
|
|
if (
|
|
Object.prototype.hasOwnProperty.call(CallerSdkTypeEnum, callerSdkType)
|
|
) {
|
|
await dcFetch(
|
|
'http://localhost',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
true, // _isUsingGen is true
|
|
callerSdkType as CallerSdkType,
|
|
false
|
|
);
|
|
|
|
let expectedHeaderRegex: RegExp;
|
|
if (
|
|
callerSdkType === CallerSdkTypeEnum.Generated ||
|
|
callerSdkType === CallerSdkTypeEnum.Base
|
|
) {
|
|
expectedHeaderRegex = RegExp(`js\/gen`);
|
|
} else {
|
|
expectedHeaderRegex = RegExp(`js\/${callerSdkType.toLowerCase()}`);
|
|
}
|
|
expect(
|
|
fakeFetchImpl.calledWithMatch(
|
|
'http://localhost',
|
|
sinon.match.hasNested(
|
|
'headers.X-Goog-Api-Client',
|
|
sinon.match(expectedHeaderRegex)
|
|
)
|
|
)
|
|
).to.be.true;
|
|
}
|
|
}
|
|
});
|
|
it('should call credentials include if using emulator on cloud workstation', async () => {
|
|
const json = {
|
|
code: 200,
|
|
message1: 'success'
|
|
};
|
|
const fakeFetchImpl = mockFetch(json, false);
|
|
await dcFetch(
|
|
'https://abc.cloudworkstations.dev',
|
|
{
|
|
name: 'n',
|
|
operationName: 'n',
|
|
variables: {}
|
|
},
|
|
{} as AbortController,
|
|
null,
|
|
null,
|
|
null,
|
|
true, // _isUsingGen is true
|
|
CallerSdkTypeEnum.Base,
|
|
true
|
|
);
|
|
expect(fakeFetchImpl).to.have.been.calledWithMatch(
|
|
'https://abc.cloudworkstations.dev',
|
|
{ credentials: 'include' }
|
|
);
|
|
});
|
|
});
|