Files
6434c30541 Repo refactoring (#5345)
* Split storage and storage-compat (#5271)

* separate storage-compat from storage

* commit

* fix tests

* wat?

* build storage-compat

* save

* fixing some compat tests

* format

* update import path

* format

* get compat tests to work

* format

* update package json

* cleanup

* format

* address comments

* Update packages/storage-compat/test/unit/service.test.ts

Co-authored-by: Christina Holland <[email protected]>

* add missing dev dep

* Update packages/storage-compat/package.json

* Update packages/storage-compat/package.json

* Update deps after v8 release

* update compat version

* Split database and database-compat (#5276)

* compile database

* pass database tests

* compile and test database-compat

* pass all tests

* prettier

* cleanup

* fix lint

* address comments

* what is going on with ci

* use correct case in import path

* uppercase

* rename

* fix component name

* Repo refactoring for v9 (#5325)

* rename folders

* rename folders

* migrate app and app-compat

* migrate installations and analytics

* migrate app check

* migrate auth

* migrate functions

* migrate messaging

* migrate performance

* migrate remoteconfig

* remove v8 code

* update versions

* migrate firebase

* fix merge issues

* save

* fix firebase builds

* update changeset config

* update build scripts

* update release script

* fix functions typings

* remove redundant typings

* update path

* treat external dependencies correctly

* fix lint

* remove firestore-compat references

* fix build issues

* update build scripts

* update pkg json

* fix test

* fix some tests

* fix some tests

* fix integration tests

* fixes

* fix dep

* update ci tests

* resolve todos

* remove exp references

* docgen build

* export FirebaseError (#5349)

* firebase packaging update (#5348)

* Fei v9 main firestore (#5319)

* compile firestore

* update typings path

* compile firestore compat

* lint compat

* make test work

* get most tests work

* build

* remove special paths

* update firestore-compat pkg json

* fix merge issues

* Simplify bundles

* Prettier

* Fix Bundle compile

* Fix build

* address comments

* console build

* Fix all tests

* Prettier

* fix merge issues

* fix typo

* update paths

* app-exp

* fix lint

* correct path

* fix

* fix compat lint

* fix firestore integration

* fix workflow

* update dep

* Always block on Auth (#5340)

* Always block on Auth

* Don't block on token if already recevied

* fix lint

* build firestore

* remove memory only tests

* fix firebase integration tests

* enable more build and tests

* add firestore-compat to firebase dep

* fix auth compat class

* enable auth test

* auth package doc

* Update API reports

Co-authored-by: Sebastian Schmidt <[email protected]>
Co-authored-by: Feiyang1 <[email protected]>

* rebasing

* Create registerMessagingCompat.ts

* Add changeset for v9 (#5350)

* add changeset

* publish messaging interop

* add firestore bumps

* update changeset

* Revert "Create registerMessagingCompat.ts"

This reverts commit 0b95a5167e403ffc0dbcd9dd7dcb7cda754e510d.

* Revert "rebasing"

This reverts commit a8bf697157aaa29e8450acb626aae81ad5b542e1.

* Update index.d.ts (#5355)

* Fix Context Check in `Messaging-Compat` (#5353)

* Add Rules Unit Testing v2 skeleton for v9 release (#5352)

* Reset package for next major version.

* Switch emulator script to node-fetch.

* Migrate the other request call to fetch.

* Update packages/rules-unit-testing/package.json

Co-authored-by: Feiyang <[email protected]>

* Create stale-ducks-live.md

* Revert version change.

Co-authored-by: Feiyang <[email protected]>

* fix import path (#5356)

* Add new types and function stubs for RUT vNext. (#5316)

* Add new types and function stubs.

* Fix types for testEnv.emulators.

* Add util functions.

* Add withFunctionTriggersDisabled overloads.

* Improve typing for EmulatorConfig.

* Fix tests.

* Rename test_environment.ts to initialize.ts.

* Add a dummy test to make CI pass.

* Implement emulators discovery in RUTv2. (#5334)

* Add new types and function stubs.

* Fix types for testEnv.emulators.

* Add util functions.

* Add withFunctionTriggersDisabled overloads.

* Improve typing for EmulatorConfig.

* Fix tests.

* Rename test_environment.ts to initialize.ts.

* Add a dummy test to make CI pass.

* Implement emulators discovery.

* Use URL object from global.

* Fix unreachable  error code.

* Implement most of RUTv2 features. (#5343)

* Implement most of RUTv2 features.

* Switch to compat instances.

* Remove legacy code.

* Use public typings for release (#5358)

* use public typings for storage

* use public typings for database

* Implement rest of RUTv2 features. (#5360)

* Implement loading rules and withFunctionTriggersDisabled.

* Implement clearFirestore and storage.

* Add missing await.

* Add default bucketUrl.

* Use alternative method to clear bucket.

* Use default param (review feedback).

* Storage typing updates (#5359)

* rename to StorageError

* more type safety

* Update API reports

* update api report

Co-authored-by: Feiyang1 <[email protected]>

* correct component name

* fix installtions-compat typing

* remove rules-unit-testing from ignore list

* reenable things

* support mjs files

* correct matching pattern

* transform only @firebase/util

* use public types for database doc

* transform all but compat packages

Co-authored-by: Sebastian Schmidt <[email protected]>
Co-authored-by: Feiyang1 <[email protected]>
Co-authored-by: kai <[email protected]>
Co-authored-by: Yuchen Shi <[email protected]>
Co-authored-by: Kai Wu <[email protected]>
2021-08-24 13:56:01 -07:00

593 lines
16 KiB
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 { expect } from 'chai';
import { EventAccumulator } from '../../database/test/helpers/EventAccumulator';
import { Reference } from '../src/api/Reference';
import { eventTestHelper } from './helpers/events';
import { getRandomNode } from './helpers/util';
describe('Order Tests', () => {
// Kind of a hack, but a lot of these tests are written such that they'll fail if run before we're
// connected to Firebase because they do a bunch of sets and then a listen and assume that they'll
// arrive in that order. But if we aren't connected yet, the "reconnection" code will send them
// in the opposite order.
beforeEach(() => {
return new Promise<void>(resolve => {
const ref = getRandomNode() as Reference;
let connected = false;
ref.root.child('.info/connected').on('value', s => {
connected = s.val() === true;
if (connected) {
resolve();
}
});
});
});
it('Push a bunch of data, enumerate it back; ensure order is correct.', async () => {
const node = getRandomNode() as Reference;
for (let i = 0; i < 10; i++) {
node.push().set(i);
}
const snap = await node.once('value');
let expected = 0;
snap.forEach(child => {
expect(child.val()).to.equal(expected);
expected++;
});
expect(expected).to.equal(10);
});
it('Push a bunch of paths, then write; ensure order is correct.', async () => {
const node = getRandomNode() as Reference;
const paths = [];
// Push them first to try to call push() multiple times in the same ms.
for (let i = 0; i < 20; i++) {
paths[i] = node.push();
}
for (let i = 0; i < 20; i++) {
paths[i].set(i);
}
const snap = await node.once('value');
let expected = 0;
snap.forEach(child => {
expect(child.val()).to.equal(expected);
expected++;
});
expect(expected).to.equal(20);
});
it('Push a bunch of data, reconnect, read it back; ensure order is chronological.', async () => {
const nodePair = getRandomNode(2) as Reference[];
let expected;
const node = nodePair[0];
let nodesSet = 0;
for (let i = 0; i < 10; i++) {
node.push().set(i, () => {
++nodesSet;
});
}
// read it back locally and make sure it's correct.
const snap = await node.once('value');
expected = 0;
snap.forEach(child => {
expect(child.val()).to.equal(expected);
expected++;
});
expect(expected).to.equal(10);
// read it back
let readSnap;
const ea = new EventAccumulator(() => readSnap);
nodePair[1].on('value', snap => {
readSnap = snap;
ea.addEvent();
});
await ea.promise;
expected = 0;
readSnap.forEach(child => {
expect(child.val()).to.equal(expected);
expected++;
});
expect(expected).to.equal(10);
});
it('Push a bunch of data with explicit priority, reconnect, read it back; ensure order is correct.', async () => {
const nodePair = getRandomNode(2) as Reference[];
let expected;
const node = nodePair[0];
let nodesSet = 0;
for (let i = 0; i < 10; i++) {
const pushedNode = node.push();
pushedNode.setWithPriority(i, 10 - i, () => {
++nodesSet;
});
}
// read it back locally and make sure it's correct.
const snap = await node.once('value');
expected = 9;
snap.forEach(child => {
expect(child.val()).to.equal(expected);
expected--;
});
expect(expected).to.equal(-1);
// local SETs are visible immediately, but the second node is in a separate repo, so it is considered remote.
// We need confirmation that the server has gotten all the data before we can expect to receive it all
// read it back
let readSnap;
const ea = new EventAccumulator(() => readSnap);
nodePair[1].on('value', snap => {
readSnap = snap;
ea.addEvent();
});
await ea.promise;
expected = 9;
readSnap.forEach(child => {
expect(child.val()).to.equal(expected);
expected--;
});
expect(expected).to.equal(-1);
});
it('Push data with exponential priority and ensure order is correct.', async () => {
const nodePair = getRandomNode(2) as Reference[];
let expected;
const node = nodePair[0];
let nodesSet = 0;
for (let i = 0; i < 10; i++) {
const pushedNode = node.push();
pushedNode.setWithPriority(
i,
111111111111111111111111111111 / Math.pow(10, i),
() => {
++nodesSet;
}
);
}
// read it back locally and make sure it's correct.
const snap = await node.once('value');
expected = 9;
snap.forEach(child => {
expect(child.val()).to.equal(expected);
expected--;
});
expect(expected).to.equal(-1);
// read it back
let readSnap;
const ea = new EventAccumulator(() => readSnap);
nodePair[1].on('value', snap => {
readSnap = snap;
ea.addEvent();
});
await ea.promise;
expected = 9;
readSnap.forEach(child => {
expect(child.val()).to.equal(expected);
expected--;
});
expect(expected).to.equal(-1);
});
it("Verify nodes without values aren't enumerated.", async () => {
const node = getRandomNode() as Reference;
node.child('foo');
node.child('bar').set('test');
let items = 0;
const snap = await node.once('value');
snap.forEach(child => {
items++;
expect(child.key).to.equal('bar');
});
expect(items).to.equal(1);
});
it.skip('Receive child_moved event when priority changes.', async () => {
const node = getRandomNode() as Reference;
// const ea = new EventAccumulator(() => eventHelper.watchesInitializedWaiter);
const eventHelper = eventTestHelper([
[node, ['child_added', 'a']],
[node, ['value', '']],
[node, ['child_added', 'b']],
[node, ['value', '']],
[node, ['child_added', 'c']],
[node, ['value', '']],
[node, ['child_moved', 'a']],
[node, ['child_changed', 'a']],
[node, ['value', '']]
]);
// await ea.promise;
node.child('a').setWithPriority('first', 1);
node.child('b').setWithPriority('second', 5);
node.child('c').setWithPriority('third', 10);
expect(eventHelper.waiter()).to.equal(false);
node.child('a').setPriority(15);
expect(eventHelper.waiter()).to.equal(true);
});
it.skip('Can reset priority to null.', async () => {
const node = getRandomNode() as Reference;
node.child('a').setWithPriority('a', 1);
node.child('b').setWithPriority('b', 2);
// const ea = new EventAccumulator(() => eventHelper.waiter());
const eventHelper = eventTestHelper([
[node, ['child_added', 'a']],
[node, ['child_added', 'b']],
[node, ['value', '']]
]);
// await ea.promise;
eventHelper.addExpectedEvents([
[node, ['child_moved', 'b']],
[node, ['child_changed', 'b']],
[node, ['value', '']]
]);
node.child('b').setPriority(null);
expect(eventHelper.waiter()).to.equal(true);
expect((await node.once('value')).child('b').getPriority()).to.equal(null);
});
it('Inserting a node under a leaf node preserves its priority.', () => {
const node = getRandomNode() as Reference;
let snap = null;
node.on('value', s => {
snap = s;
});
node.setWithPriority('a', 10);
node.child('deeper').set('deeper');
expect(snap.getPriority()).to.equal(10);
});
it('Verify order of mixed numbers / strings / no priorities.', async () => {
const nodePair = getRandomNode(2) as Reference[];
const nodeAndPriorities = [
'alpha42',
'zed',
'noPriorityC',
null,
'num41',
500,
'noPriorityB',
null,
'num80',
4000.1,
'num50',
4000,
'num10',
24,
'alpha41',
'zed',
'alpha20',
'horse',
'num20',
123,
'num70',
4000.01,
'noPriorityA',
null,
'alpha30',
'tree',
'num30',
300,
'num60',
4000.001,
'alpha10',
'0horse',
'num42',
500,
'alpha40',
'zed',
'num40',
500
];
let setsCompleted = 0;
for (let i = 0; i < nodeAndPriorities.length; i++) {
const n = nodePair[0].child(nodeAndPriorities[i++] as string);
n.setWithPriority(1, nodeAndPriorities[i], () => {
setsCompleted++;
});
}
const expectedOutput =
'noPriorityA, noPriorityB, noPriorityC, num10, num20, num30, num40, num41, num42, num50, num60, num70, num80, alpha10, alpha20, alpha30, alpha40, alpha41, alpha42, ';
const snap = await nodePair[0].once('value');
let output = '';
snap.forEach(n => {
output += n.key + ', ';
});
expect(output).to.equal(expectedOutput);
let eventsFired = false;
output = '';
nodePair[1].on('value', snap => {
snap.forEach(n => {
output += n.key + ', ';
});
expect(output).to.equal(expectedOutput);
eventsFired = true;
});
});
it('Verify order of integer keys.', async () => {
const ref = getRandomNode() as Reference;
const keys = ['foo', 'bar', '03', '0', '100', '20', '5', '3', '003', '9'];
let setsCompleted = 0;
for (let i = 0; i < keys.length; i++) {
const child = ref.child(keys[i]);
child.set(true, () => {
setsCompleted++;
});
}
const expectedOutput = '0, 3, 03, 003, 5, 9, 20, 100, bar, foo, ';
const snap = await ref.once('value');
let output = '';
snap.forEach(n => {
output += n.key + ', ';
});
expect(output).to.equal(expectedOutput);
});
it('Ensure prevName is correct on child_added event.', () => {
const node = getRandomNode() as Reference;
let added = '';
node.on('child_added', (snap, prevName) => {
added += snap.key + ' ' + prevName + ', ';
});
node.set({ a: 1, b: 2, c: 3 });
expect(added).to.equal('a null, b a, c b, ');
});
it('Ensure prevName is correct when adding new nodes.', () => {
const node = getRandomNode() as Reference;
let added = '';
node.on('child_added', (snap, prevName) => {
added += snap.key + ' ' + prevName + ', ';
});
node.set({ b: 2, c: 3, d: 4 });
expect(added).to.equal('b null, c b, d c, ');
added = '';
node.child('a').set(1);
expect(added).to.equal('a null, ');
added = '';
node.child('e').set(5);
expect(added).to.equal('e d, ');
});
it('Ensure prevName is correct when adding new nodes with JSON.', () => {
const node = getRandomNode() as Reference;
let added = '';
node.on('child_added', (snap, prevName) => {
added += snap.key + ' ' + prevName + ', ';
});
node.set({ b: 2, c: 3, d: 4 });
expect(added).to.equal('b null, c b, d c, ');
added = '';
node.set({ a: 1, b: 2, c: 3, d: 4 });
expect(added).to.equal('a null, ');
added = '';
node.set({ a: 1, b: 2, c: 3, d: 4, e: 5 });
expect(added).to.equal('e d, ');
});
it('Ensure prevName is correct when moving nodes.', () => {
const node = getRandomNode() as Reference;
let moved = '';
node.on('child_moved', (snap, prevName) => {
moved += snap.key + ' ' + prevName + ', ';
});
node.child('a').setWithPriority('a', 1);
node.child('b').setWithPriority('b', 2);
node.child('c').setWithPriority('c', 3);
node.child('d').setWithPriority('d', 4);
node.child('d').setPriority(0);
expect(moved).to.equal('d null, ');
moved = '';
node.child('a').setPriority(4);
expect(moved).to.equal('a c, ');
moved = '';
node.child('c').setPriority(0.5);
expect(moved).to.equal('c d, ');
});
it('Ensure prevName is correct when moving nodes by setting whole JSON.', () => {
const node = getRandomNode() as Reference;
let moved = '';
node.on('child_moved', (snap, prevName) => {
moved += snap.key + ' ' + prevName + ', ';
});
node.set({
a: { '.value': 'a', '.priority': 1 },
b: { '.value': 'b', '.priority': 2 },
c: { '.value': 'c', '.priority': 3 },
d: { '.value': 'd', '.priority': 4 }
});
node.set({
d: { '.value': 'd', '.priority': 0 },
a: { '.value': 'a', '.priority': 1 },
b: { '.value': 'b', '.priority': 2 },
c: { '.value': 'c', '.priority': 3 }
});
expect(moved).to.equal('d null, ');
moved = '';
node.set({
d: { '.value': 'd', '.priority': 0 },
b: { '.value': 'b', '.priority': 2 },
c: { '.value': 'c', '.priority': 3 },
a: { '.value': 'a', '.priority': 4 }
});
expect(moved).to.equal('a c, ');
moved = '';
node.set({
d: { '.value': 'd', '.priority': 0 },
c: { '.value': 'c', '.priority': 0.5 },
b: { '.value': 'b', '.priority': 2 },
a: { '.value': 'a', '.priority': 4 }
});
expect(moved).to.equal('c d, ');
});
it('Case 595: Should not get child_moved event when deleting prioritized grandchild.', () => {
const f = getRandomNode() as Reference;
let moves = 0;
f.on('child_moved', () => {
moves++;
});
f.child('test/foo').setWithPriority(42, '5');
f.child('test/foo2').setWithPriority(42, '10');
f.child('test/foo').remove();
f.child('test/foo2').remove();
expect(moves).to.equal(0, 'Should *not* have received any move events.');
});
it('Can set value with priority of 0.', () => {
const f = getRandomNode() as Reference;
let snap = null;
f.on('value', s => {
snap = s;
});
f.setWithPriority('test', 0);
expect(snap.getPriority()).to.equal(0);
});
it('Can set object with priority of 0.', () => {
const f = getRandomNode() as Reference;
let snap = null;
f.on('value', s => {
snap = s;
});
f.setWithPriority({ x: 'test', y: 7 }, 0);
expect(snap.getPriority()).to.equal(0);
});
it('Case 2003: Should get child_moved for any priority change, regardless of whether it affects ordering.', () => {
const f = getRandomNode() as Reference;
const moved = [];
f.on('child_moved', snap => {
moved.push(snap.key);
});
f.set({
a: { '.value': 'a', '.priority': 0 },
b: { '.value': 'b', '.priority': 1 },
c: { '.value': 'c', '.priority': 2 },
d: { '.value': 'd', '.priority': 3 }
});
expect(moved).to.deep.equal([]);
f.child('b').setWithPriority('b', 1.5);
expect(moved).to.deep.equal(['b']);
});
it('Case 2003: Should get child_moved for any priority change, regardless of whether it affects ordering (2).', () => {
const f = getRandomNode() as Reference;
const moved = [];
f.on('child_moved', snap => {
moved.push(snap.key);
});
f.set({
a: { '.value': 'a', '.priority': 0 },
b: { '.value': 'b', '.priority': 1 },
c: { '.value': 'c', '.priority': 2 },
d: { '.value': 'd', '.priority': 3 }
});
expect(moved).to.deep.equal([]);
f.set({
a: { '.value': 'a', '.priority': 0 },
b: { '.value': 'b', '.priority': 1.5 },
c: { '.value': 'c', '.priority': 2 },
d: { '.value': 'd', '.priority': 3 }
});
expect(moved).to.deep.equal(['b']);
});
});