voluptasmollitia
Version:
Monorepo for the Firebase JavaScript SDK
492 lines (430 loc) • 14.2 kB
text/typescript
/**
* @license
* Copyright 2017 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 * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as request from 'request';
import * as sinon from 'sinon';
import * as firebase from '../src/api';
import { base64 } from '@firebase/util';
import { _FirebaseApp } from '@firebase/app-types/private';
const expect = chai.expect;
before(() => {
chai.use(chaiAsPromised);
});
describe('Testing Module Tests', function () {
let sandbox: sinon.SinonSandbox;
beforeEach(function () {
sandbox = sinon.createSandbox();
});
afterEach(function () {
sandbox && sandbox.restore();
});
it('assertSucceeds() iff success', async function () {
const success = Promise.resolve('success');
const failure = Promise.reject('failure');
await firebase.assertSucceeds(success).catch(() => {
throw new Error('Expected success to succeed.');
});
await firebase
.assertSucceeds(failure)
.then(() => {
throw new Error('Expected failure to fail.');
})
.catch(() => {});
});
it('assertFails() iff PERMISSION_DENIED', async function () {
const success = Promise.resolve('success');
const permissionDenied = Promise.reject({
message: 'PERMISSION_DENIED'
});
const otherFailure = Promise.reject('failure');
await firebase
.assertFails(success)
.then(() => {
throw new Error('Expected success to fail.');
})
.catch(() => {});
await firebase.assertFails(permissionDenied).catch(() => {
throw new Error('Expected permissionDenied to succeed.');
});
await firebase
.assertFails(otherFailure)
.then(() => {
throw new Error('Expected otherFailure to fail.');
})
.catch(() => {});
});
it('assertFails() if code is permission-denied', async function () {
const success = Promise.resolve('success');
const permissionDenied = Promise.reject({
code: 'permission-denied'
});
const otherFailure = Promise.reject('failure');
await firebase
.assertFails(success)
.then(() => {
throw new Error('Expected success to fail.');
})
.catch(() => {});
await firebase.assertFails(permissionDenied).catch(() => {
throw new Error('Expected permissionDenied to succeed.');
});
await firebase
.assertFails(otherFailure)
.then(() => {
throw new Error('Expected otherFailure to fail.');
})
.catch(() => {});
});
it('assertFails() if code is PERMISSION_DENIED', async function () {
const success = Promise.resolve('success');
const permissionDenied = Promise.reject({
code: 'PERMISSION_DENIED'
});
const otherFailure = Promise.reject('failure');
await firebase
.assertFails(success)
.then(() => {
throw new Error('Expected success to fail.');
})
.catch(() => {});
await firebase.assertFails(permissionDenied).catch(() => {
throw new Error('Expected permissionDenied to succeed.');
});
await firebase
.assertFails(otherFailure)
.then(() => {
throw new Error('Expected otherFailure to fail.');
})
.catch(() => {});
});
it('assertFails() if message is Permission denied', async function () {
const success = Promise.resolve('success');
const permissionDenied = Promise.reject({
message: 'Permission denied'
});
const otherFailure = Promise.reject('failure');
await firebase
.assertFails(success)
.then(() => {
throw new Error('Expected success to fail.');
})
.catch(() => {});
await firebase.assertFails(permissionDenied).catch(() => {
throw new Error('Expected permissionDenied to succeed.');
});
await firebase
.assertFails(otherFailure)
.then(() => {
throw new Error('Expected otherFailure to fail.');
})
.catch(() => {});
});
it('discoverEmulators() finds all running emulators', async () => {
const options = await firebase.discoverEmulators();
expect(options).to.deep.equal({
database: {
host: 'localhost',
port: 9002
},
firestore: {
host: 'localhost',
port: 9003
},
storage: {
host: 'localhost',
port: 9199
},
hub: {
host: 'localhost',
port: 4400
}
});
});
it('initializeTestApp() with auth=null does not set access token', async function () {
const app = firebase.initializeTestApp({
projectId: 'foo',
auth: undefined
});
const authInternal = ((app as unknown) as _FirebaseApp).container
.getProvider('auth-internal')
.getImmediate({ optional: true });
// Auth instance will not be available because no API Key is provided
expect(authInternal).to.be.null;
});
it('initializeTestApp() with auth sets the correct access token', async function () {
const auth = { uid: 'alice' };
const app = firebase.initializeTestApp({
projectId: 'foo',
auth: auth
});
const authInternal = ((app as unknown) as _FirebaseApp).container
.getProvider('auth-internal')
.getImmediate();
const token = await authInternal.getToken();
expect(token).to.have.keys('accessToken');
const claims = JSON.parse(
base64.decodeString(token!.accessToken.split('.')[1], /*webSafe=*/ false)
);
// We add an 'iat' field.
expect(claims).to.deep.equal({
iss: 'https://securetoken.google.com/foo',
aud: 'foo',
iat: 0,
exp: 3600,
auth_time: 0,
sub: 'alice',
user_id: 'alice',
firebase: {
sign_in_provider: 'custom',
identities: {}
}
});
});
it('initializeAdminApp() has admin access to RTDB', async function () {
await firebase.loadDatabaseRules({
databaseName: 'foo',
rules: '{ "rules": {".read": false, ".write": false} }'
});
const app = firebase.initializeAdminApp({
projectId: 'foo',
databaseName: 'foo',
storageBucket: 'foo'
});
await firebase.assertSucceeds(
app.database().ref().child('/foo/bar').set({ hello: 'world' })
);
});
it('initializeAdminApp() has admin access to Firestore', async function () {
await firebase.loadFirestoreRules({
projectId: 'foo',
rules: `service cloud.firestore {
match /databases/{db}/documents/{doc=**} {
allow read, write: if false;
}
}`
});
const app = firebase.initializeAdminApp({
projectId: 'foo',
databaseName: 'foo',
storageBucket: 'foo'
});
await firebase.assertSucceeds(
app.firestore().doc('/foo/bar').set({ hello: 'world' })
);
});
it('initializeAdminApp() has admin access to storage', async function () {
await firebase.loadStorageRules({
rules: `rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if false;
}
}
}`
});
const app = firebase.initializeAdminApp({
projectId: 'foo',
databaseName: 'foo',
storageBucket: 'foo'
});
// TODO: This test cannot be enabled without adding credentials to the test environment
// due to an underlying issue with firebase-admin storage. For now we will run it
// locally but not in CI.
if (process.env.CI !== "true") {
await firebase.assertSucceeds(
app.storage().bucket().file('/foo/bar.txt').save('Hello, World!')
);
}
});
it('initializeAdminApp() and initializeTestApp() work together', async function () {
await firebase.loadDatabaseRules({
databaseName: 'foo',
rules: JSON.stringify({
'rules': {
'public': { '.read': true, '.write': true },
'private': { '.read': false, '.write': false }
}
})
});
const adminApp = firebase.initializeAdminApp({
projectId: 'foo',
databaseName: 'foo',
storageBucket: 'foo'
});
const testApp = firebase.initializeTestApp({
projectId: 'foo',
databaseName: 'foo',
storageBucket: 'foo'
});
// Admin app can write anywhere
await firebase.assertSucceeds(
adminApp.database().ref().child('/public/doc').set({ hello: 'admin' })
);
await firebase.assertSucceeds(
adminApp.database().ref().child('/private/doc').set({ hello: 'admin' })
);
// Test app can only write to public, not to private
await firebase.assertSucceeds(
testApp.database().ref().child('/public/doc').set({ hello: 'test' })
);
await firebase.assertFails(
testApp.database().ref().child('/private/doc').set({ hello: 'test' })
);
});
it('initializeAdminApp() works with custom claims', async function () {
await firebase.loadFirestoreRules({
projectId: 'foo',
rules: `service cloud.firestore {
match /databases/{db}/documents/{doc=**} {
allow read, write: if request.auth.token.custom_claim == 'foo';
}
}`
});
const noClaim = firebase.initializeTestApp({
projectId: 'foo',
auth: {
uid: 'noClaim'
}
});
const hasClaim = firebase.initializeTestApp({
projectId: 'foo',
auth: {
uid: 'hasClaim',
custom_claim: 'foo'
}
});
const wrongClaim = firebase.initializeTestApp({
projectId: 'foo',
auth: {
uid: 'wrongClaim',
custom_claim: 'bar'
}
});
await firebase.assertSucceeds(
hasClaim.firestore().doc('test/test').set({ hello: 'test' })
);
await firebase.assertFails(
noClaim.firestore().doc('test/test').set({ hello: 'test' })
);
await firebase.assertFails(
wrongClaim.firestore().doc('test/test').set({ hello: 'test' })
);
});
it('initializeTestApp() does not destroy user input', function () {
const options = {
projectId: 'fakeproject',
auth: {
uid: 'sam',
email: 'sam@sam.com'
}
};
const optionsCopy = Object.assign({}, options);
firebase.initializeTestApp(options);
expect(options).to.deep.equal(optionsCopy);
});
it('loadDatabaseRules() throws if no databaseName or rules', async function () {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await expect(
firebase.loadDatabaseRules({} as any)
).to.eventually.be.rejectedWith(/databaseName not specified/);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
await expect(
firebase.loadDatabaseRules({
databaseName: 'foo'
} as any)
).to.eventually.be.rejectedWith(/must provide rules/);
await expect(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
firebase.loadDatabaseRules({ rules: '{}' } as any)
).to.eventually.be.rejectedWith(/databaseName not specified/);
});
it('loadDatabaseRules() succeeds on valid input', async function () {
await firebase.loadDatabaseRules({
databaseName: 'foo',
rules: '{ "rules": {} }'
});
});
it('loadFirestoreRules() succeeds on valid input', async function () {
await firebase.loadFirestoreRules({
projectId: 'foo',
rules: `service cloud.firestore {
match /databases/{db}/documents/{doc=**} {
allow read, write;
}
}`
});
});
it('loadStorageRules() succeeds on valid input', async function () {
await firebase.loadStorageRules({
rules: `rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if false;
}
}
}`
});
});
it('loadStorageRules() fails on invalid input', async function () {
const p = firebase.loadStorageRules({
rules: `rules_version = '2';
service firebase.storage {
banana
}`
});
await expect(p).to.eventually.be.rejected;
});
it('clearFirestoreData() succeeds on valid input', async function () {
await firebase.clearFirestoreData({
projectId: 'foo'
});
});
it('apps() returns apps created with initializeTestApp', async function () {
const numApps = firebase.apps().length;
await firebase.initializeTestApp({ databaseName: 'foo', auth: undefined });
expect(firebase.apps().length).to.equal(numApps + 1);
await firebase.initializeTestApp({ databaseName: 'bar', auth: undefined });
expect(firebase.apps().length).to.equal(numApps + 2);
});
it('there is a way to get database timestamps', function () {
expect(firebase.database.ServerValue.TIMESTAMP).to.deep.equal({
'.sv': 'timestamp'
});
});
it('there is a way to get firestore timestamps', function () {
expect(firebase.firestore.FieldValue.serverTimestamp()).not.to.be.null;
});
it('disabling function triggers does not throw, returns value', async function () {
const putSpy = sandbox.spy(request, 'put');
const res = await firebase.withFunctionTriggersDisabled(() => {
return Promise.resolve(1234);
});
expect(res).to.eq(1234);
expect(putSpy.callCount).to.equal(2);
});
it('disabling function triggers always re-enables, event when the function throws', async function () {
const putSpy = sandbox.spy(request, 'put');
const res = firebase.withFunctionTriggersDisabled(() => {
throw new Error('I throw!');
});
await expect(res).to.eventually.be.rejectedWith('I throw!');
expect(putSpy.callCount).to.equal(2);
});
});