Files
firebase-ios-sdk/Firestore/Example/Tests/Integration/API/FIRAggregateTests.mm
T

1138 lines
42 KiB
Plaintext

/*
* Copyright 2023 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 <FirebaseFirestore/FIRAggregateField.h>
#import <FirebaseFirestore/FIRFieldPath.h>
#import <FirebaseFirestore/FirebaseFirestore.h>
#import <XCTest/XCTest.h>
#import "Firestore/Example/Tests/Util/FSTIntegrationTestCase.h"
#import "Firestore/core/src/util/exception.h"
@interface FIRAggregateTests : FSTIntegrationTestCase
@end
@implementation FIRAggregateTests
- (void)testCountAggregateFieldQueryEquals {
FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
FIRAggregateQuery* query1 = [coll1 aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query1Same =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query1DiffAgg =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a"
isEqualTo:@1] queryLimitedTo:100] count];
FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
isEqualTo:@1] queryOrderedByField:@"c"] count];
XCTAssertEqualObjects(query1, query1Same);
XCTAssertEqualObjects(query2, query2Same);
XCTAssertEqualObjects(query3, query3Same);
XCTAssertEqual([query1 hash], [query1Same hash]);
XCTAssertEqual([query2 hash], [query2Same hash]);
XCTAssertEqual([query3 hash], [query3Same hash]);
XCTAssertFalse([query1 isEqual:nil]);
XCTAssertFalse([query1 isEqual:@"string"]);
XCTAssertFalse([query1 isEqual:query2]);
XCTAssertFalse([query2 isEqual:query3]);
XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
XCTAssertNotEqual([query1 hash], [query2 hash]);
XCTAssertNotEqual([query2 hash], [query3 hash]);
}
- (void)testSumAggregateFieldQueryEquals {
FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
FIRAggregateQuery* query1 =
[coll1 aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query1Same =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query1WithFieldPath =
[coll1Same aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfFieldPath:[[FIRFieldPath alloc]
initWithFields:@[ @"baz" ]]] ]];
FIRAggregateQuery* query1DiffAgg =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
isEqualTo:@1] queryOrderedByField:@"c"]
aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
XCTAssertEqualObjects(query1, query1Same);
XCTAssertEqualObjects(query1, query1WithFieldPath);
XCTAssertEqualObjects(query2, query2Same);
XCTAssertEqualObjects(query3, query3Same);
XCTAssertEqual([query1 hash], [query1Same hash]);
XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
XCTAssertEqual([query2 hash], [query2Same hash]);
XCTAssertEqual([query3 hash], [query3Same hash]);
XCTAssertFalse([query1 isEqual:nil]);
XCTAssertFalse([query1 isEqual:@"string"]);
XCTAssertFalse([query1 isEqual:query2]);
XCTAssertFalse([query2 isEqual:query3]);
XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
XCTAssertNotEqual([query1 hash], [query2 hash]);
XCTAssertNotEqual([query2 hash], [query3 hash]);
}
- (void)testAverageAggregateFieldQueryEquals {
FIRCollectionReference* coll1 = [self collectionRefWithDocuments:@{}];
FIRCollectionReference* coll1Same = [[coll1 firestore] collectionWithPath:[coll1 path]];
FIRAggregateQuery* query1 =
[coll1 aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
FIRAggregateQuery* query1Same =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
FIRAggregateQuery* query1WithFieldPath = [coll1Same aggregate:@[
[FIRAggregateField
aggregateFieldForAverageOfFieldPath:[[FIRFieldPath alloc] initWithFields:@[ @"baz" ]]]
]];
FIRAggregateQuery* query1DiffAgg =
[coll1Same aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRCollectionReference* sub = [[coll1 documentWithPath:@"bar"] collectionWithPath:@"baz"];
FIRAggregateQuery* query2 = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
FIRAggregateQuery* query2Same = [[[sub queryWhereField:@"a" isEqualTo:@1] queryLimitedTo:100]
aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
FIRAggregateQuery* query3 = [[[sub queryWhereField:@"b" isEqualTo:@1] queryOrderedByField:@"c"]
aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
FIRAggregateQuery* query3Same = [[[sub queryWhereField:@"b"
isEqualTo:@1] queryOrderedByField:@"c"]
aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
XCTAssertEqualObjects(query1, query1Same);
XCTAssertEqualObjects(query1, query1WithFieldPath);
XCTAssertEqualObjects(query2, query2Same);
XCTAssertEqualObjects(query3, query3Same);
XCTAssertEqual([query1 hash], [query1Same hash]);
XCTAssertEqual([query1 hash], [query1WithFieldPath hash]);
XCTAssertEqual([query2 hash], [query2Same hash]);
XCTAssertEqual([query3 hash], [query3Same hash]);
XCTAssertFalse([query1 isEqual:nil]);
XCTAssertFalse([query1 isEqual:@"string"]);
XCTAssertFalse([query1 isEqual:query2]);
XCTAssertFalse([query2 isEqual:query3]);
XCTAssertNotEqual([query1 hash], [query1DiffAgg hash]);
XCTAssertNotEqual([query1 hash], [query2 hash]);
XCTAssertNotEqual([query2 hash], [query3 hash]);
}
- (void)testAggregateFieldQueryNotEquals {
FIRCollectionReference* coll = [self collectionRefWithDocuments:@{}];
FIRAggregateQuery* query1 = [coll aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query2 =
[coll aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query3 =
[coll aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
XCTAssertNotEqualObjects(query1, query2);
XCTAssertNotEqualObjects(query2, query3);
XCTAssertNotEqualObjects(query3, query1);
XCTAssertNotEqual([query1 hash], [query2 hash]);
XCTAssertNotEqual([query2 hash], [query3 hash]);
XCTAssertNotEqual([query3 hash], [query1 hash]);
FIRQuery* baseQuery = [[[[coll documentWithPath:@"bar"] collectionWithPath:@"baz"]
queryWhereField:@"a"
isEqualTo:@1] queryLimitedTo:100];
FIRAggregateQuery* query4 = [baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForCount] ]];
FIRAggregateQuery* query5 =
[baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForSumOfField:@"baz"] ]];
FIRAggregateQuery* query6 =
[baseQuery aggregate:@[ [FIRAggregateField aggregateFieldForAverageOfField:@"baz"] ]];
XCTAssertNotEqualObjects(query4, query5);
XCTAssertNotEqualObjects(query5, query6);
XCTAssertNotEqualObjects(query6, query4);
XCTAssertNotEqual([query4 hash], [query5 hash]);
XCTAssertNotEqual([query5 hash], [query6 hash]);
XCTAssertNotEqual([query6 hash], [query4 hash]);
}
- (void)testCanRunAggregateQuery {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
]]];
// Count
XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
[NSNumber numberWithLong:2L]);
XCTAssertEqual([snapshot count], [NSNumber numberWithLong:2L]);
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
[NSNumber numberWithLong:150L], );
// Average
XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"pages"]],
[NSNumber numberWithDouble:75.0]);
}
- (void)testCanRunEmptyAggregateQuery {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuery* emptyQuery = [testCollection aggregate:@[]];
__block NSError* result;
XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
[emptyQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
XCTAssertNil(snapshot);
result = error;
[expectation fulfill];
}];
[self awaitExpectation:expectation];
XCTAssertNotNil(result);
XCTAssertTrue([[result localizedDescription] containsString:@"Aggregations can not be empty"]);
}
// (TODO:b/283101111): Try thread sanitizer to see if timeout on Github Actions is gone.
#if !defined(THREAD_SANITIZER)
- (void)testAggregateFieldQuerySnapshotEquality {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot1 =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
]]];
FIRAggregateQuerySnapshot* snapshot2 =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
]]];
// different aggregates
FIRAggregateQuerySnapshot* snapshot3 =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"weight"],
[FIRAggregateField aggregateFieldForAverageOfField:@"weight"]
]]];
// different data set
FIRAggregateQuerySnapshot* snapshot4 =
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@50]
aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField
aggregateFieldForAverageOfField:@"pages"]
]]];
XCTAssertEqualObjects(snapshot1, snapshot2);
XCTAssertNotEqualObjects(snapshot1, snapshot3);
XCTAssertNotEqualObjects(snapshot1, snapshot4);
XCTAssertNotEqualObjects(snapshot3, snapshot4);
XCTAssertEqual([snapshot1 hash], [snapshot2 hash]);
XCTAssertNotEqual([snapshot1 hash], [snapshot3 hash]);
XCTAssertNotEqual([snapshot1 hash], [snapshot4 hash]);
XCTAssertNotEqual([snapshot3 hash], [snapshot4 hash]);
}
#endif // #if !defined(THREAD_SANITIZER)
- (void)testAggregateOnFieldNameWithMaxLength {
// The longest field name and alias allowed is 1500 bytes or 1499 characters.
NSString* longField = [@"" stringByPaddingToLength:1499
withString:@"0123456789"
startingAtIndex:0];
FIRCollectionReference* testCollection =
[self collectionRefWithDocuments:@{@"a" : @{longField : @1}, @"b" : @{longField : @2}}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:longField] ]]];
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:longField]],
[NSNumber numberWithLong:3], );
}
- (void)testCanGetDuplicateAggregations {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"]
]]];
// Count
XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
[NSNumber numberWithLong:2L]);
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
[NSNumber numberWithLong:150L], );
}
- (void)testTerminateDoesNotCrashWithFlyingAggregateQuery {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuery* aggQuery = [testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
]];
__block FIRAggregateQuerySnapshot* result;
XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
[aggQuery aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
XCTAssertNil(error);
result = snapshot;
[expectation fulfill];
}];
[self awaitExpectation:expectation];
// Count
XCTAssertEqual([result valueForAggregateField:[FIRAggregateField aggregateFieldForCount]],
[NSNumber numberWithLong:2L]);
// Sum
XCTAssertEqual(
[result valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
[NSNumber numberWithLong:150L], );
}
- (void)testCannotPerformMoreThanMaxAggregations {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
// Max is 5, we're attempting 6. I also like to live dangerously.
FIRAggregateQuery* query = [testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForSumOfField:@"weight"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"weight"],
[FIRAggregateField aggregateFieldForAverageOfField:@"foo"]
]];
__block NSError* result;
XCTestExpectation* expectation = [self expectationWithDescription:@"aggregate result"];
[query aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
XCTAssertNil(snapshot);
result = error;
[expectation fulfill];
}];
[self awaitExpectation:expectation];
XCTAssertNotNil(result);
XCTAssertTrue([[result localizedDescription] containsString:@"maximum number of aggregations"]);
}
- (void)testThrowsAnErrorWhenGettingTheResultOfAnUnrequestedAggregation {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"pages"] ]]];
@try {
[snapshot count];
XCTAssertTrue(false, "Exception expected");
} @catch (NSException* exception) {
XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
XCTAssertEqualObjects(exception.reason,
@"'count()' was not requested in the aggregation query.");
}
@
try {
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"foo"]];
XCTAssertTrue(false, "Exception expected");
} @catch (NSException* exception) {
XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
XCTAssertEqualObjects(exception.reason,
@"'sum(foo)' was not requested in the aggregation query.");
}
@
try {
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]];
XCTAssertTrue(false, "Exception expected");
} @catch (NSException* exception) {
XCTAssertEqualObjects(exception.name, @"FIRInvalidArgumentException");
XCTAssertEqualObjects(exception.reason,
@"'avg(pages)' was not requested in the aggregation query.");
}
}
- (void)testPerformsAggregationWhenUsingInOperator {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"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
}
}];
FIRAggregateQuerySnapshot* snapshot = [self
readSnapshotForAggregate:[[testCollection queryWhereField:@"rating" in:@[ @5, @3 ]]
aggregate:@[
[FIRAggregateField aggregateFieldForSumOfField:@"rating"],
[FIRAggregateField aggregateFieldForAverageOfField:@"rating"],
[FIRAggregateField aggregateFieldForCount]
]]];
// Count
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
longValue],
8L);
// Average
XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"rating"]]
doubleValue],
4.0);
}
- (void)testPerformsAggregationsOnNestedMapValues {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"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}}
},
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"metadata.pages"],
[FIRAggregateField aggregateFieldForCount]
]]];
// Count
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 2L);
// Sum
XCTAssertEqual(
[[snapshot
valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"metadata.pages"]]
longValue],
150L);
// Average
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"metadata.pages"]]
doubleValue],
75.0);
}
- (void)testPerformsSumThatOverflowsMaxLong {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"rating" : [NSNumber numberWithLong:LLONG_MAX]
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"rating" : [NSNumber numberWithLong:LLONG_MAX]
},
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithLong:LLONG_MAX] doubleValue] +
[[NSNumber numberWithLong:LLONG_MAX] doubleValue]);
}
- (void)testPerformsSumThatCanOverflowLongValuesDuringAccumulation {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"rating" : [NSNumber numberWithLong:LLONG_MAX]
},
@"b" : @{@"author" : @"authorB", @"title" : @"titleB", @"rating" : [NSNumber numberWithLong:1]},
@"c" :
@{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
longLongValue],
[[NSNumber numberWithLong:LLONG_MAX - 100] longLongValue]);
}
- (void)testPerformsSumThatIsNegative {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"rating" : [NSNumber numberWithLong:LLONG_MAX]
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"rating" : [NSNumber numberWithLong:-LLONG_MAX]
},
@"c" :
@{@"author" : @"authorC", @"title" : @"titleC", @"rating" : [NSNumber numberWithLong:-101]},
@"d" : @{
@"author" : @"authorD",
@"title" : @"titleD",
@"rating" : [NSNumber numberWithLong:-10000]
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
longLongValue],
[[NSNumber numberWithLong:-10101] longLongValue]);
}
- (void)testPerformsSumThatIsPositiveInfinity {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"rating" : [NSNumber numberWithDouble:DBL_MAX]
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"rating" : [NSNumber numberWithDouble:DBL_MAX]
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
[NSNumber numberWithDouble:INFINITY]);
}
- (void)testPerformsSumThatIsNegativeInfinity {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"rating" : [NSNumber numberWithDouble:-DBL_MAX]
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"rating" : [NSNumber numberWithDouble:-DBL_MAX]
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]],
[NSNumber numberWithDouble:-INFINITY]);
}
- (void)testPerformsSumThatIsValidButCouldOverflowDuringAggregation {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
@"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
@"c" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]},
@"d" : @{@"rating" : [NSNumber numberWithDouble:-DBL_MAX]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
long long ratingL =
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
longLongValue];
XCTAssertTrue(ratingL == [[NSNumber numberWithDouble:-INFINITY] longLongValue] || ratingL == 0 ||
ratingL == [[NSNumber numberWithDouble:INFINITY] longLongValue]);
double ratingD =
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
doubleValue];
XCTAssertTrue(ratingD == -INFINITY || ratingD == 0 || ratingD == INFINITY);
}
- (void)testPerformsSumOverResultSetOfZeroDocuments {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"pages"] ]]];
// Sum
XCTAssertEqual(
[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"pages"]],
[NSNumber numberWithLong:0L]);
}
- (void)testPerformsSumOnlyOnNumericFields {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithLong:5]},
@"b" : @{@"rating" : [NSNumber numberWithLong:4]},
@"c" : @{@"rating" : @"3"},
@"d" : @{@"rating" : [NSNumber numberWithLong:1]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"rating"]
]]];
// Count
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
longLongValue],
[[NSNumber numberWithLong:10] longLongValue]);
}
- (void)testPerformsSumOfMinIEEE754 {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection
aggregate:@[ [FIRAggregateField
aggregateFieldForSumOfField:@"rating"] ]]];
// Sum
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForSumOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
}
- (void)testPerformsAverageOfVariousNumericTypes {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"x" : @1,
@"intToInt" : [NSNumber numberWithLong:10],
@"floatToInt" : [NSNumber numberWithDouble:10.5],
@"mixedToInt" : [NSNumber numberWithLong:10],
@"floatToFloat" : [NSNumber numberWithDouble:5.5],
@"mixedToFloat" : [NSNumber numberWithDouble:8.6],
@"intToFloat" : [NSNumber numberWithLong:10]
},
@"b" : @{
@"intToInt" : [NSNumber numberWithLong:5],
@"floatToInt" : [NSNumber numberWithDouble:9.5],
@"mixedToInt" : [NSNumber numberWithDouble:9.5],
@"floatToFloat" : [NSNumber numberWithDouble:4.5],
@"mixedToFloat" : [NSNumber numberWithLong:9],
@"intToFloat" : [NSNumber numberWithLong:9]
},
@"c" : @{
@"intToInt" : [NSNumber numberWithLong:0],
@"floatToInt" : @"ignore",
@"mixedToInt" : [NSNumber numberWithDouble:10.5],
@"floatToFloat" : [NSNumber numberWithDouble:3.5],
@"mixedToFloat" : [NSNumber numberWithLong:10],
@"intToFloat" : @"ignore"
}
}];
NSArray* testCases = @[
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToInt"],
@"expected" : [NSNumber numberWithLong:5]
},
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToInt"],
@"expected" : [NSNumber numberWithLong:10]
},
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToInt"],
@"expected" : [NSNumber numberWithLong:10]
},
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"floatToFloat"],
@"expected" : [NSNumber numberWithDouble:4.5]
},
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"mixedToFloat"],
@"expected" : [NSNumber numberWithDouble:9.2]
},
@{
@"agg" : [FIRAggregateField aggregateFieldForAverageOfField:@"intToFloat"],
@"expected" : [NSNumber numberWithDouble:9.5]
}
];
for (NSDictionary* testCase in testCases) {
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[ testCase[@"agg"] ]]];
// Average
XCTAssertEqual([[snapshot valueForAggregateField:testCase[@"agg"]] longValue],
[testCase[@"expected"] longLongValue]);
XCTAssertEqualWithAccuracy([[snapshot valueForAggregateField:testCase[@"agg"]] doubleValue],
[testCase[@"expected"] doubleValue], 0.00000000000001);
}
}
- (void)testPerformsAverageCausingUnderflow {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]},
@"b" : @{@"rating" : [NSNumber numberWithDouble:0]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
]]];
// Average
XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithDouble:0] doubleValue]);
}
- (void)testPerformsAverageOfMinIEEE754 {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithDouble:__DBL_DENORM_MIN__]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
]]];
// Average
XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithDouble:__DBL_DENORM_MIN__] doubleValue]);
}
- (void)testPerformsAverageOverflowIEEE754DuringAccumulation {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]},
@"b" : @{@"rating" : [NSNumber numberWithDouble:DBL_MAX]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
]]];
// Average
XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithDouble:INFINITY] doubleValue]);
}
- (void)testPerformsAverageOverResultSetOfZeroDocuments {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{
@"author" : @"authorA",
@"title" : @"titleA",
@"pages" : @100,
@"height" : @24.5,
@"weight" : @24.1,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
},
@"b" : @{
@"author" : @"authorB",
@"title" : @"titleB",
@"pages" : @50,
@"height" : @25.5,
@"weight" : @75.5,
@"foo" : @1,
@"bar" : @2,
@"baz" : @3
}
}];
FIRAggregateQuerySnapshot* snapshot = [self
readSnapshotForAggregate:[[testCollection queryWhereField:@"pages" isGreaterThan:@200]
aggregate:@[ [FIRAggregateField
aggregateFieldForAverageOfField:@"pages"] ]]];
// Average
XCTAssertEqual([snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"pages"]],
[NSNull null]);
}
- (void)testPerformsAverageOnlyOnNumericFields {
FIRCollectionReference* testCollection = [self collectionRefWithDocuments:@{
@"a" : @{@"rating" : [NSNumber numberWithLong:5]},
@"b" : @{@"rating" : [NSNumber numberWithLong:4]},
@"c" : @{@"rating" : @"3"},
@"d" : @{@"rating" : [NSNumber numberWithLong:6]}
}];
FIRAggregateQuerySnapshot* snapshot =
[self readSnapshotForAggregate:[testCollection aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForAverageOfField:@"rating"]
]]];
// Count
XCTAssertEqual(
[[snapshot valueForAggregateField:[FIRAggregateField aggregateFieldForCount]] longValue], 4L);
// Average
XCTAssertEqual([[snapshot valueForAggregateField:[FIRAggregateField
aggregateFieldForAverageOfField:@"rating"]]
doubleValue],
[[NSNumber numberWithDouble:5] doubleValue]);
}
- (void)testFailWithMessageWithConsoleLinkIfMissingIndex {
XCTSkipIf([FSTIntegrationTestCase isRunningAgainstEmulator],
"Skip this test when running against the Firestore emulator because the Firestore "
"emulator does not use indexes and never fails with a 'missing index' error.");
FIRCollectionReference* testCollection = [self collectionRef];
FIRQuery* compositeIndexQuery = [[testCollection queryWhereField:@"field1"
isEqualTo:@42] queryWhereField:@"field2"
isLessThan:@99];
FIRAggregateQuery* compositeIndexAggregateQuery = [compositeIndexQuery aggregate:@[
[FIRAggregateField aggregateFieldForCount],
[FIRAggregateField aggregateFieldForSumOfField:@"pages"],
[FIRAggregateField aggregateFieldForAverageOfField:@"pages"]
]];
XCTestExpectation* queryCompletion = [self expectationWithDescription:@"query"];
[compositeIndexAggregateQuery
aggregationWithSource:FIRAggregateSourceServer
completion:^(FIRAggregateQuerySnapshot* snapshot, NSError* error) {
XCTAssertNotNil(error);
if (error) {
NSString* errorDescription = [error localizedDescription];
XCTAssertTrue([errorDescription.lowercaseString containsString:@"index"],
"The NSError should have contained the word 'index' "
"(case-insensitive), but got: %@",
errorDescription);
// TODO(b/316359394) Remove this check for the default databases once
// cl/582465034 is rolled out to production.
if ([[FSTIntegrationTestCase databaseID] isEqualToString:@"(default)"]) {
XCTAssertTrue(
[errorDescription containsString:@"https://console.firebase.google.com"],
"The NSError should have contained the string "
"'https://console.firebase.google.com', but got: %@",
errorDescription);
}
}
XCTAssertNil(snapshot);
[queryCompletion fulfill];
}];
[self awaitExpectations];
}
@end