mirror of
https://github.com/hanzoai/firebase-js-sdk.git
synced 2026-09-24 16:59:10 +00:00
1420 lines
39 KiB
TypeScript
1420 lines
39 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2022 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 { it } from '../../util/mocha_extensions';
|
|
import {
|
|
collection,
|
|
collectionGroup,
|
|
disableNetwork,
|
|
doc,
|
|
orderBy,
|
|
DocumentData,
|
|
getCountFromServer,
|
|
getAggregateFromServer,
|
|
query,
|
|
QueryDocumentSnapshot,
|
|
terminate,
|
|
where,
|
|
writeBatch,
|
|
count,
|
|
sum,
|
|
average,
|
|
addDoc
|
|
} from '../util/firebase_export';
|
|
import {
|
|
apiDescribe,
|
|
withEmptyTestCollection,
|
|
withTestCollection,
|
|
withTestDb
|
|
} from '../util/helpers';
|
|
|
|
apiDescribe('Count queries', persistence => {
|
|
it('can run count query getCountFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getCountFromServer(coll);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
['so!@#$%^&*()_+special/sub', 'b1/so!@#$%^&*()_+special'].forEach(
|
|
documentPath => {
|
|
it(
|
|
'can run count query getCountFromServer with special chars in the document path: ' +
|
|
documentPath,
|
|
() => {
|
|
return withTestCollection(persistence, {}, async coll => {
|
|
const subColl1 = collection(coll, documentPath);
|
|
await addDoc(subColl1, { foo: 'bar' });
|
|
await addDoc(subColl1, { foo: 'baz' });
|
|
const snapshot1 = await getCountFromServer(subColl1);
|
|
expect(snapshot1.data().count).to.equal(2);
|
|
});
|
|
}
|
|
);
|
|
}
|
|
);
|
|
|
|
it("count query doesn't use converter", () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
const throwingConverter = {
|
|
toFirestore(obj: never): DocumentData {
|
|
throw new Error('should never be called');
|
|
},
|
|
fromFirestore(snapshot: QueryDocumentSnapshot): never {
|
|
throw new Error('should never be called');
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const query_ = query(
|
|
coll,
|
|
where('author', '==', 'authorA')
|
|
).withConverter(throwingConverter);
|
|
const snapshot = await getCountFromServer(query_);
|
|
expect(snapshot.data().count).to.equal(1);
|
|
});
|
|
});
|
|
|
|
it('count query supports collection groups', () => {
|
|
return withTestDb(persistence, async db => {
|
|
const collectionGroupId = doc(collection(db, 'aggregateQueryTest')).id;
|
|
const docPaths = [
|
|
`${collectionGroupId}/cg-doc1`,
|
|
`abc/123/${collectionGroupId}/cg-doc2`,
|
|
`zzz${collectionGroupId}/cg-doc3`,
|
|
`abc/123/zzz${collectionGroupId}/cg-doc4`,
|
|
`abc/123/zzz/${collectionGroupId}`
|
|
];
|
|
const batch = writeBatch(db);
|
|
for (const docPath of docPaths) {
|
|
batch.set(doc(db, docPath), { x: 1 });
|
|
}
|
|
await batch.commit();
|
|
const snapshot = await getCountFromServer(
|
|
collectionGroup(db, collectionGroupId)
|
|
);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('getCountFromServer fails if firestore is terminated', () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
await terminate(firestore);
|
|
expect(() => getCountFromServer(coll)).to.throw(
|
|
'The client has already been terminated.'
|
|
);
|
|
});
|
|
});
|
|
|
|
it("terminate doesn't crash when there is count query in flight", () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
void getCountFromServer(coll);
|
|
await terminate(firestore);
|
|
});
|
|
});
|
|
|
|
// TODO(b/277628384): Re-enable this test once b/277628384 is fixed.
|
|
// eslint-disable-next-line no-restricted-properties
|
|
it.skip('getCountFromServer fails if user is offline', () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
await disableNetwork(firestore);
|
|
await expect(getCountFromServer(coll)).to.be.eventually.rejectedWith(
|
|
'Failed to get aggregate result because the client is offline'
|
|
);
|
|
});
|
|
});
|
|
|
|
// Only verify the error message for missing indexes when running against
|
|
// production, since the Firestore Emulator does not require index creation
|
|
// and will, therefore, never fail in this situation.
|
|
// eslint-disable-next-line no-restricted-properties
|
|
it.skipEnterprise.skipEmulator(
|
|
'getCountFromServer error message contains console link if missing index',
|
|
() => {
|
|
return withEmptyTestCollection(persistence, async coll => {
|
|
const query_ = query(
|
|
coll,
|
|
where('key1', '==', 42),
|
|
where('key2', '<', 42)
|
|
);
|
|
// TODO(b/316359394) Remove the special logic for non-default databases
|
|
// once cl/582465034 is rolled out to production.
|
|
// @ts-ignore internal API usage
|
|
if (coll.firestore._databaseId.isDefaultDatabase) {
|
|
await expect(
|
|
getCountFromServer(query_)
|
|
).to.be.eventually.rejectedWith(
|
|
/index.*https:\/\/console\.firebase\.google\.com/
|
|
);
|
|
} else {
|
|
await expect(getCountFromServer(query_)).to.be.eventually.rejected;
|
|
}
|
|
});
|
|
}
|
|
);
|
|
});
|
|
|
|
apiDescribe('Aggregation queries', persistence => {
|
|
it('can run count query getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
count: count()
|
|
});
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('can alias aggregations using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
foo: count(),
|
|
'with.dots': count()
|
|
});
|
|
expect(snapshot.data().foo).to.equal(2);
|
|
expect(snapshot.data()['with.dots']).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('allows special chars in aliases when using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
'with-un/su+pp[or]ted': count()
|
|
});
|
|
|
|
expect(snapshot.data()['with-un/su+pp[or]ted']).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('allows backticks in aliases when using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
'`with-un/su+pp[or]ted`': count()
|
|
});
|
|
|
|
expect(snapshot.data()['`with-un/su+pp[or]ted`']).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('allows backslash in aliases when using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
'with\\backslash\\es': count()
|
|
});
|
|
|
|
expect(snapshot.data()['with\\backslash\\es']).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('allows aliases with length greater than 1500 bytes', () => {
|
|
// Alias string length is bytes of UTF-8 encoded alias + 1;
|
|
let longAlias = '';
|
|
for (let i = 0; i < 150; i++) {
|
|
longAlias += '0123456789';
|
|
}
|
|
|
|
const longerAlias = longAlias + longAlias;
|
|
|
|
const testDocs = {
|
|
a: { num: 3 },
|
|
b: { num: 5 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
[longAlias]: count(),
|
|
[longerAlias]: count()
|
|
});
|
|
|
|
expect(snapshot.data()[longAlias]).to.equal(2);
|
|
expect(snapshot.data()[longerAlias]).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('can get duplicate aggregations using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
count: count(),
|
|
foo: count()
|
|
});
|
|
expect(snapshot.data().foo).to.equal(2);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it("getAggregationFromServer doesn't use converter", () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA' },
|
|
b: { author: 'authorB', title: 'titleB' }
|
|
};
|
|
const throwingConverter = {
|
|
toFirestore(obj: never): DocumentData {
|
|
throw new Error('should never be called');
|
|
},
|
|
fromFirestore(snapshot: QueryDocumentSnapshot): never {
|
|
throw new Error('should never be called');
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const query_ = query(
|
|
coll,
|
|
where('author', '==', 'authorA')
|
|
).withConverter(throwingConverter);
|
|
const snapshot = await getAggregateFromServer(query_, { count: count() });
|
|
expect(snapshot.data().count).to.equal(1);
|
|
});
|
|
});
|
|
|
|
it('aggregate query supports collection groups', () => {
|
|
return withTestDb(persistence, async db => {
|
|
const collectionGroupId = doc(collection(db, 'aggregateQueryTest')).id;
|
|
const docPaths = [
|
|
`${collectionGroupId}/cg-doc1`,
|
|
`abc/123/${collectionGroupId}/cg-doc2`,
|
|
`zzz${collectionGroupId}/cg-doc3`,
|
|
`abc/123/zzz${collectionGroupId}/cg-doc4`,
|
|
`abc/123/zzz/${collectionGroupId}`
|
|
];
|
|
const batch = writeBatch(db);
|
|
for (const docPath of docPaths) {
|
|
batch.set(doc(db, docPath), { x: 1 });
|
|
}
|
|
await batch.commit();
|
|
const snapshot = await getAggregateFromServer(
|
|
collectionGroup(db, collectionGroupId),
|
|
{ count: count() }
|
|
);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('getAggregateFromServer fails if firestore is terminated', () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
await terminate(firestore);
|
|
expect(() => getAggregateFromServer(coll, { count: count() })).to.throw(
|
|
'The client has already been terminated.'
|
|
);
|
|
});
|
|
});
|
|
|
|
it("terminate doesn't crash when there is aggregate query in flight", () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
void getAggregateFromServer(coll, { count: count() });
|
|
await terminate(firestore);
|
|
});
|
|
});
|
|
|
|
// TODO(b/277628384): Re-enable this test once b/277628384 is fixed.
|
|
// eslint-disable-next-line no-restricted-properties
|
|
it.skip('getAggregateFromServer fails if user is offline', () => {
|
|
return withEmptyTestCollection(persistence, async (coll, firestore) => {
|
|
await disableNetwork(firestore);
|
|
await expect(getCountFromServer(coll)).to.be.eventually.rejectedWith(
|
|
'Failed to get aggregate result because the client is offline'
|
|
);
|
|
});
|
|
});
|
|
|
|
// Only verify the error message for missing indexes when running against
|
|
// production, since the Firestore Emulator does not require index creation
|
|
// and will, therefore, never fail in this situation.
|
|
// eslint-disable-next-line no-restricted-properties
|
|
it.skipEmulator.skipEnterprise(
|
|
'getAggregateFromServer error message contains console link good if missing index',
|
|
() => {
|
|
return withEmptyTestCollection(persistence, async coll => {
|
|
const query_ = query(
|
|
coll,
|
|
where('key1', '==', 42),
|
|
where('key2', '<', 42)
|
|
);
|
|
// TODO(b/316359394) Remove the special logic for non-default databases
|
|
// once cl/582465034 is rolled out to production.
|
|
// @ts-ignore internal API usage
|
|
if (coll.firestore._databaseId.isDefaultDatabase) {
|
|
await expect(
|
|
getAggregateFromServer(query_, {
|
|
count: count()
|
|
})
|
|
).to.be.eventually.rejectedWith(
|
|
/index.*https:\/\/console\.firebase\.google\.com/
|
|
);
|
|
} else {
|
|
await expect(
|
|
getAggregateFromServer(query_, {
|
|
count: count()
|
|
})
|
|
).to.be.eventually.rejected;
|
|
}
|
|
});
|
|
}
|
|
);
|
|
});
|
|
|
|
apiDescribe('Aggregation queries - sum / average', persistence => {
|
|
it('can run sum query getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(
|
|
query(coll, orderBy('pages')),
|
|
{
|
|
totalPages: sum('pages')
|
|
}
|
|
);
|
|
expect(snapshot.data().totalPages).to.equal(150);
|
|
});
|
|
});
|
|
|
|
it('can run average query getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averagePages: average('pages')
|
|
});
|
|
expect(snapshot.data().averagePages).to.equal(75);
|
|
});
|
|
});
|
|
|
|
it('can get multiple aggregations using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalPages: sum('pages'),
|
|
averagePages: average('pages'),
|
|
count: count()
|
|
});
|
|
expect(snapshot.data().totalPages).to.equal(150);
|
|
expect(snapshot.data().averagePages).to.equal(75);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('can get duplicate aggregations using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalPages: sum('pages'),
|
|
averagePages: average('pages'),
|
|
totalPagesX: sum('pages'),
|
|
averagePagesY: average('pages')
|
|
});
|
|
expect(snapshot.data().totalPages).to.equal(150);
|
|
expect(snapshot.data().averagePages).to.equal(75);
|
|
expect(snapshot.data().totalPagesX).to.equal(150);
|
|
expect(snapshot.data().averagePagesY).to.equal(75);
|
|
});
|
|
});
|
|
|
|
it('can perform max (5) aggregations using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalPages: sum('pages'),
|
|
averagePages: average('pages'),
|
|
count: count(),
|
|
totalPagesX: sum('pages'),
|
|
averagePagesY: average('pages')
|
|
});
|
|
expect(snapshot.data().totalPages).to.equal(150);
|
|
expect(snapshot.data().averagePages).to.equal(75);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
expect(snapshot.data().totalPagesX).to.equal(150);
|
|
expect(snapshot.data().averagePagesY).to.equal(75);
|
|
});
|
|
});
|
|
|
|
it.skipEnterprise(
|
|
'fails when exceeding the max (5) aggregations using getAggregationFromServer',
|
|
() => {
|
|
const testDocs = {
|
|
a: { author: 'authorA', title: 'titleA', pages: 100 },
|
|
b: { author: 'authorB', title: 'titleB', pages: 50 }
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const promise = getAggregateFromServer(coll, {
|
|
totalPages: sum('pages'),
|
|
averagePages: average('pages'),
|
|
count: count(),
|
|
totalPagesX: sum('pages'),
|
|
averagePagesY: average('pages'),
|
|
countZ: count()
|
|
});
|
|
|
|
await expect(promise).to.eventually.be.rejectedWith(
|
|
/maximum number of aggregations/
|
|
);
|
|
});
|
|
}
|
|
);
|
|
|
|
it('returns undefined when getting the result of an unrequested aggregation', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 3
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating'),
|
|
averageRating: average('rating')
|
|
});
|
|
|
|
// @ts-expect-error
|
|
const totalPages = snapshot.data().totalPages;
|
|
expect(totalPages).to.equal(undefined);
|
|
});
|
|
});
|
|
|
|
it('performs aggregates when using `in` operator getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 3
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(
|
|
query(coll, where('rating', 'in', [5, 3])),
|
|
{
|
|
totalRating: sum('rating'),
|
|
averageRating: average('rating'),
|
|
countOfDocs: count()
|
|
}
|
|
);
|
|
expect(snapshot.data().totalRating).to.equal(8);
|
|
expect(snapshot.data().averageRating).to.equal(4);
|
|
expect(snapshot.data().countOfDocs).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('performs aggregations on nested map values using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
metadata: { pages: 100, rating: { critic: 2, user: 5 } }
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
metadata: { pages: 50, rating: { critic: 4, user: 4 } }
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalPages: sum('metadata.pages'),
|
|
averagePages: average('metadata.pages'),
|
|
count: count()
|
|
});
|
|
expect(snapshot.data().totalPages).to.equal(150);
|
|
expect(snapshot.data().averagePages).to.equal(75);
|
|
expect(snapshot.data().count).to.equal(2);
|
|
});
|
|
});
|
|
|
|
it('performs sum that results in float using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4.5
|
|
},
|
|
c: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 3
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(12.5);
|
|
});
|
|
});
|
|
|
|
it('performs sum of ints and floats that results in an int using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4.5
|
|
},
|
|
c: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 3.5
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(13);
|
|
});
|
|
});
|
|
|
|
it('performs sum that overflows max int using getAggregationFromServer', () => {
|
|
// A large value that will be represented as a Long on the server, but
|
|
// doubling (2x) this value must overflow Long and force the result to be
|
|
// represented as a Double type on the server.
|
|
const maxLong = Math.pow(2, 63) - 1;
|
|
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: maxLong
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: maxLong
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(maxLong + maxLong);
|
|
});
|
|
});
|
|
|
|
it('performs sum that can overflow integer values during accumulation using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_SAFE_INTEGER
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 1
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: -101
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(
|
|
Number.MAX_SAFE_INTEGER - 100
|
|
);
|
|
});
|
|
});
|
|
|
|
it('performs sum that is negative using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_SAFE_INTEGER
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: Number.MIN_SAFE_INTEGER
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: -101
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: -10000
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(-10101);
|
|
});
|
|
});
|
|
|
|
it('performs sum that is positive infinity using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: Number.MAX_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(Number.POSITIVE_INFINITY);
|
|
});
|
|
});
|
|
|
|
it('performs sum that is positive infinity using getAggregationFromServer v2', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 1e293
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(Number.POSITIVE_INFINITY);
|
|
});
|
|
});
|
|
|
|
it('performs sum that is negative infinity using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: -Number.MAX_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: -Number.MAX_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(Number.NEGATIVE_INFINITY);
|
|
});
|
|
});
|
|
|
|
it('performs sum that is valid but could overflow during aggregation using getAggregationFromServer', () => {
|
|
// Sum of rating would be 0, but if the accumulation overflow, we expect infinity
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: Number.MAX_VALUE
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: -Number.MAX_VALUE
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: -Number.MAX_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.oneOf([
|
|
Number.POSITIVE_INFINITY,
|
|
Number.NEGATIVE_INFINITY,
|
|
0
|
|
]);
|
|
});
|
|
});
|
|
|
|
it('performs sum that includes NaN using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.NaN
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.be.NaN;
|
|
});
|
|
});
|
|
|
|
it.skipEnterprise(
|
|
'performs sum over a result set of zero documents using getAggregationFromServer',
|
|
() => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 3
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(
|
|
query(coll, where('pages', '>', 200)),
|
|
{
|
|
totalPages: sum('pages')
|
|
}
|
|
);
|
|
expect(snapshot.data().totalPages).to.equal(0);
|
|
});
|
|
}
|
|
);
|
|
|
|
it('performs sum only on numeric fields using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: '3'
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 1
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating'),
|
|
countOfDocs: count()
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(10);
|
|
expect(snapshot.data().countOfDocs).to.equal(4);
|
|
});
|
|
});
|
|
|
|
it('performs sum of min IEEE754 using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MIN_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
totalRating: sum('rating')
|
|
});
|
|
expect(snapshot.data().totalRating).to.equal(Number.MIN_VALUE);
|
|
});
|
|
});
|
|
|
|
it('performs average of ints that results in an int using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 10
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 5
|
|
},
|
|
c: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(5);
|
|
});
|
|
});
|
|
|
|
it('performs average of floats that results in an int using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 10.5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 9.5
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(10);
|
|
});
|
|
});
|
|
|
|
it('performs average of floats and ints that results in an int using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 10
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 9.5
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 10.5
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(10);
|
|
});
|
|
});
|
|
|
|
it('performs average of float that results in float using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5.5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4.5
|
|
},
|
|
c: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 3.5
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(4.5);
|
|
});
|
|
});
|
|
|
|
it('performs average of floats and ints that results in a float using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 8.6
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 9
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 150,
|
|
year: 2021,
|
|
rating: 10
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.be.approximately(9.2, 0.0000001);
|
|
});
|
|
});
|
|
|
|
it('performs average of ints that results in a float using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 10
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 9
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(9.5);
|
|
});
|
|
});
|
|
|
|
it('performs average causing underflow using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MIN_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(0);
|
|
});
|
|
});
|
|
|
|
it('performs average of min IEEE754 using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MIN_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(Number.MIN_VALUE);
|
|
});
|
|
});
|
|
|
|
it('performs average that overflows IEEE754 during accumulation using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.MAX_VALUE
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: Number.MAX_VALUE
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(Number.POSITIVE_INFINITY);
|
|
});
|
|
});
|
|
|
|
it('performs average that includes NaN using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: Number.NaN
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating')
|
|
});
|
|
expect(snapshot.data().averageRating).to.be.NaN;
|
|
});
|
|
});
|
|
|
|
it('performs average over a result set of zero documents using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 3
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 0
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(
|
|
query(coll, where('pages', '>', 200)),
|
|
{
|
|
averagePages: average('pages')
|
|
}
|
|
);
|
|
expect(snapshot.data().averagePages).to.be.null;
|
|
});
|
|
});
|
|
|
|
it('performs average only on numeric fields using getAggregationFromServer', () => {
|
|
const testDocs = {
|
|
a: {
|
|
author: 'authorA',
|
|
title: 'titleA',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: 5
|
|
},
|
|
b: {
|
|
author: 'authorB',
|
|
title: 'titleB',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 4
|
|
},
|
|
c: {
|
|
author: 'authorC',
|
|
title: 'titleC',
|
|
pages: 100,
|
|
year: 1980,
|
|
rating: '3'
|
|
},
|
|
d: {
|
|
author: 'authorD',
|
|
title: 'titleD',
|
|
pages: 50,
|
|
year: 2020,
|
|
rating: 6
|
|
}
|
|
};
|
|
return withTestCollection(persistence, testDocs, async coll => {
|
|
const snapshot = await getAggregateFromServer(coll, {
|
|
averageRating: average('rating'),
|
|
countOfDocs: count()
|
|
});
|
|
expect(snapshot.data().averageRating).to.equal(5);
|
|
expect(snapshot.data().countOfDocs).to.equal(4);
|
|
});
|
|
});
|
|
});
|