aboutsummaryrefslogtreecommitdiffhomepage
path: root/Example/Storage/Tests/Unit
diff options
context:
space:
mode:
Diffstat (limited to 'Example/Storage/Tests/Unit')
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageDeleteTests.m164
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageGetMetadataTests.m188
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageMetadataTests.m282
-rw-r--r--Example/Storage/Tests/Unit/FIRStoragePathTests.m234
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageReferenceTests.m163
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageTestHelpers.h127
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageTestHelpers.m128
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageTests.m214
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageTokenAuthorizerTests.m226
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageUpdateMetadataTests.m199
-rw-r--r--Example/Storage/Tests/Unit/FIRStorageUtilsTests.m125
11 files changed, 2050 insertions, 0 deletions
diff --git a/Example/Storage/Tests/Unit/FIRStorageDeleteTests.m b/Example/Storage/Tests/Unit/FIRStorageDeleteTests.m
new file mode 100644
index 0000000..42a3b1a
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageDeleteTests.m
@@ -0,0 +1,164 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageDeleteTask.h"
+#import "FIRStorageTestHelpers.h"
+
+@interface FIRStorageDeleteTests : XCTestCase
+
+@property(strong, nonatomic) GTMSessionFetcherService *fetcherService;
+@property(strong, nonatomic) FIRStorageMetadata *metadata;
+@property(strong, nonatomic) FIRStorage *storage;
+@property(strong, nonatomic) id mockApp;
+
+@end
+
+@implementation FIRStorageDeleteTests
+
+- (void)setUp {
+ [super setUp];
+
+ NSDictionary *metadataDict = @{ @"bucket" : @"bucket", @"name" : @"path/to/object" };
+ self.metadata = [[FIRStorageMetadata alloc] initWithDictionary:metadataDict];
+
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket.appspot.com");
+
+ self.mockApp = OCMClassMock([FIRApp class]);
+ OCMStub([self.mockApp name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)self.mockApp options]).andReturn(mockOptions);
+
+ self.fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcherService.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:self.mockApp
+ fetcherService:self.fetcherService];
+
+ self.storage = [FIRStorage storageForApp:self.mockApp];
+}
+
+- (void)tearDown {
+ self.fetcherService = nil;
+ self.storage = nil;
+ self.mockApp = nil;
+ [super tearDown];
+}
+
+- (void)testFetcherConfiguration {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock =
+ ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertEqualObjects(fetcher.request.URL, [FIRStorageTestHelpers objectURL]);
+#pragma clang diagnostic pop
+ XCTAssertEqualObjects(fetcher.request.HTTPMethod, @"DELETE");
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageDeleteTask *task = [[FIRStorageDeleteTask alloc] initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(NSError *error) {
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testSuccessfulFetch {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers successBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageDeleteTask *task = [[FIRStorageDeleteTask alloc] initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(NSError *error) {
+ XCTAssertEqual(error, nil);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthenticated {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthenticated"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthenticatedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageDeleteTask *task =
+ [[FIRStorageDeleteTask alloc] initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(NSError *error) {
+ XCTAssertEqual(error.code,
+ FIRStorageErrorCodeUnauthenticated);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthorized {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthorized"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthorizedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageDeleteTask *task =
+ [[FIRStorageDeleteTask alloc] initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(NSError *error) {
+ XCTAssertEqual(error.code,
+ FIRStorageErrorCodeUnauthorized);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchObjectDoesntExist {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchObjectDoesntExist"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers notFoundBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers notFoundPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageDeleteTask *task =
+ [[FIRStorageDeleteTask alloc] initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(NSError *error) {
+ XCTAssertEqual(error.code,
+ FIRStorageErrorCodeObjectNotFound);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageGetMetadataTests.m b/Example/Storage/Tests/Unit/FIRStorageGetMetadataTests.m
new file mode 100644
index 0000000..72a057d
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageGetMetadataTests.m
@@ -0,0 +1,188 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageGetMetadataTask.h"
+#import "FIRStorageTestHelpers.h"
+
+@interface FIRStorageGetMetadataTests : XCTestCase
+
+@property(strong, nonatomic) GTMSessionFetcherService *fetcherService;
+@property(strong, nonatomic) FIRStorageMetadata *metadata;
+@property(strong, nonatomic) FIRStorage *storage;
+@property(strong, nonatomic) id mockApp;
+
+@end
+
+@implementation FIRStorageGetMetadataTests
+
+- (void)setUp {
+ [super setUp];
+
+ NSDictionary *metadataDict = @{ @"bucket" : @"bucket", @"name" : @"path/to/object" };
+ self.metadata = [[FIRStorageMetadata alloc] initWithDictionary:metadataDict];
+
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket.appspot.com");
+
+ self.mockApp = OCMClassMock([FIRApp class]);
+ OCMStub([self.mockApp name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)self.mockApp options]).andReturn(mockOptions);
+
+ self.fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcherService.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:self.mockApp
+ fetcherService:self.fetcherService];
+
+ self.storage = [FIRStorage storageForApp:self.mockApp];
+}
+
+- (void)tearDown {
+ self.fetcherService = nil;
+ self.storage = nil;
+ self.mockApp = nil;
+ [super tearDown];
+}
+
+- (void)testFetcherConfiguration {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock =
+ ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertEqualObjects(fetcher.request.URL, [FIRStorageTestHelpers objectURL]);
+#pragma clang diagnostic pop
+ XCTAssertEqualObjects(fetcher.request.HTTPMethod, @"GET");
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testSuccessfulFetch {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers successBlockWithMetadata:self.metadata];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ XCTAssertEqualObjects(self.metadata.bucket, metadata.bucket);
+ XCTAssertEqualObjects(self.metadata.name, metadata.name);
+ XCTAssertEqual(error, nil);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthenticated {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthenticated"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthenticatedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ XCTAssertEqual(metadata, nil);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnauthenticated);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthorized {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthorized"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthorizedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ XCTAssertEqual(metadata, nil);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnauthorized);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchObjectDoesntExist {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchObjectDoesntExist"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers notFoundBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers notFoundPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ XCTAssertEqual(metadata, nil);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeObjectNotFound);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchBadJSON {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchBadJSON"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers invalidJSONBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageGetMetadataTask *task = [[FIRStorageGetMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ completion:^(FIRStorageMetadata *metadata, NSError *error) {
+ XCTAssertEqual(metadata, nil);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnknown);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m b/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m
new file mode 100644
index 0000000..f5fb3b3
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageMetadataTests.m
@@ -0,0 +1,282 @@
+// Copyright 2017 Google
+//
+// 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 <XCTest/XCTest.h>
+
+#import "FIRStorageMetadata.h"
+#import "FIRStorageMetadata_Private.h"
+#import "FIRStorageUtils.h"
+
+@interface FIRStorageMetadataTests : XCTestCase
+
+@end
+
+@implementation FIRStorageMetadataTests
+
+- (void)testInitialzeNoMetadata {
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:@{}];
+ XCTAssertNotNil(metadata);
+}
+
+- (void)testInitialzeFullMetadata {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataCacheControl : @"max-age=3600, no-cache",
+ kFIRStorageMetadataContentDisposition : @"inline",
+ kFIRStorageMetadataContentEncoding : @"gzip",
+ kFIRStorageMetadataContentLanguage : @"en-us",
+ kFIRStorageMetadataContentType : @"application/octet-stream",
+ kFIRStorageMetadataCustomMetadata : @{@"foo" : @{@"bar" : @"baz"}},
+ kFIRStorageMetadataDownloadTokens : @"1234567890",
+ kFIRStorageMetadataGeneration : @"12345",
+ kFIRStorageMetadataMetageneration : @"67890",
+ kFIRStorageMetadataName : @"path/to/object",
+ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108Z",
+ kFIRStorageMetadataUpdated : @"2016-03-01T20:16:01.673Z"
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata);
+ XCTAssertEqualObjects(metadata.bucket, metaDict[kFIRStorageMetadataBucket]);
+ XCTAssertEqualObjects(metadata.cacheControl, metaDict[kFIRStorageMetadataCacheControl]);
+ XCTAssertEqualObjects(metadata.contentDisposition,
+ metaDict[kFIRStorageMetadataContentDisposition]);
+ XCTAssertEqualObjects(metadata.contentEncoding, metaDict[kFIRStorageMetadataContentEncoding], );
+ XCTAssertEqualObjects(metadata.contentType, metaDict[kFIRStorageMetadataContentType]);
+ XCTAssertEqualObjects(metadata.customMetadata, metaDict[kFIRStorageMetadataCustomMetadata]);
+ NSString *URLFormat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@";
+ NSString *URLString = [NSString
+ stringWithFormat:URLFormat, metaDict[kFIRStorageMetadataBucket],
+ [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]],
+ metaDict[kFIRStorageMetadataDownloadTokens]];
+ XCTAssertEqualObjects([metadata.downloadURL description], URLString);
+ NSString *generation = [NSString stringWithFormat:@"%lld", metadata.generation];
+ XCTAssertEqualObjects(generation, metaDict[kFIRStorageMetadataGeneration]);
+ NSString *metageneration = [NSString stringWithFormat:@"%lld", metadata.metageneration];
+ XCTAssertEqualObjects(metageneration, metaDict[kFIRStorageMetadataMetageneration]);
+ XCTAssertEqualObjects(metadata.path, metaDict[kFIRStorageMetadataName]);
+ XCTAssertEqualObjects([metadata RFC3339StringFromDate:metadata.timeCreated],
+ metaDict[kFIRStorageMetadataTimeCreated]);
+ XCTAssertEqualObjects([metadata RFC3339StringFromDate:metadata.updated],
+ metaDict[kFIRStorageMetadataUpdated]);
+}
+
+- (void)testDictionaryRepresentation {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataCacheControl : @"max-age=3600, no-cache",
+ kFIRStorageMetadataContentDisposition : @"inline",
+ kFIRStorageMetadataContentEncoding : @"gzip",
+ kFIRStorageMetadataContentLanguage : @"en-us",
+ kFIRStorageMetadataContentType : @"application/octet-stream",
+ kFIRStorageMetadataCustomMetadata : @{@"foo" : @{@"bar" : @"baz"}},
+ kFIRStorageMetadataDownloadTokens : @"1234567890",
+ kFIRStorageMetadataGeneration : @"12345",
+ kFIRStorageMetadataMetageneration : @"67890",
+ kFIRStorageMetadataName : @"path/to/object",
+ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108Z",
+ kFIRStorageMetadataUpdated : @"2016-03-01T20:16:01.673Z"
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ NSDictionary *dictRepresentation = [metadata dictionaryRepresentation];
+ XCTAssertNotEqual(dictRepresentation, nil);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataBucket],
+ metaDict[kFIRStorageMetadataBucket]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataCacheControl],
+ metaDict[kFIRStorageMetadataCacheControl]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataContentDisposition],
+ metaDict[kFIRStorageMetadataContentDisposition]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataContentEncoding],
+ metaDict[kFIRStorageMetadataContentEncoding]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataContentLanguage],
+ metaDict[kFIRStorageMetadataContentLanguage]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataContentType],
+ metaDict[kFIRStorageMetadataContentType]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataCustomMetadata],
+ metaDict[kFIRStorageMetadataCustomMetadata]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataDownloadTokens],
+ metaDict[kFIRStorageMetadataDownloadTokens]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataGeneration],
+ metaDict[kFIRStorageMetadataGeneration]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataMetageneration],
+ metaDict[kFIRStorageMetadataMetageneration]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataName],
+ metaDict[kFIRStorageMetadataName]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataTimeCreated],
+ metaDict[kFIRStorageMetadataTimeCreated]);
+ XCTAssertEqualObjects(dictRepresentation[kFIRStorageMetadataUpdated],
+ metaDict[kFIRStorageMetadataUpdated]);
+}
+
+- (void)testInitialzeNoDownloadTokensGetToken {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata);
+ XCTAssertEqual(metadata.downloadURL, nil);
+ XCTAssertEqual(metadata.downloadURLs, nil);
+}
+
+- (void)testInitialzeMultipleDownloadTokensGetToken {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataDownloadTokens : @"12345,67890",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata);
+ NSString *URLformat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@";
+ NSString *URLString0 = [NSString
+ stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket],
+ [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]],
+ @"12345"];
+ NSString *URLString1 = [NSString
+ stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket],
+ [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]],
+ @"67890"];
+ XCTAssertEqualObjects([metadata.downloadURL absoluteString], URLString0);
+ XCTAssertEqualObjects([metadata.downloadURLs[0] absoluteString], URLString0);
+ XCTAssertEqualObjects([metadata.downloadURLs[1] absoluteString], URLString1);
+}
+
+- (void)testMultipleDownloadURLsGetToken {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ NSString *URLformat = @"https://firebasestorage.googleapis.com/v0/b/%@/o/%@?alt=media&token=%@";
+ NSString *URLString0 = [NSString
+ stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket],
+ [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]],
+ @"12345"];
+ NSString *URLString1 = [NSString
+ stringWithFormat:URLformat, metaDict[kFIRStorageMetadataBucket],
+ [FIRStorageUtils GCSEscapedString:metaDict[kFIRStorageMetadataName]],
+ @"67890"];
+ NSURL *URL0 = [NSURL URLWithString:URLString0];
+ NSURL *URL1 = [NSURL URLWithString:URLString1];
+ NSArray *downloadURLs = @[ URL0, URL1 ];
+ [metadata setValue:downloadURLs forKey:@"downloadURLs"];
+ NSDictionary *newMetaDict = metadata.dictionaryRepresentation;
+ XCTAssertEqualObjects(newMetaDict[kFIRStorageMetadataDownloadTokens], @"12345,67890");
+}
+
+- (void)testInitialzeMetadataWithFile {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/file",
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ [metadata setType:FIRStorageMetadataTypeFile];
+ XCTAssertEqual(metadata.isFile, YES);
+ XCTAssertEqual(metadata.isFolder, NO);
+}
+
+- (void)testInitialzeMetadataWithFolder {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/folder/",
+ };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ [metadata setType:FIRStorageMetadataTypeFolder];
+ XCTAssertEqual(metadata.isFolder, YES);
+ XCTAssertEqual(metadata.isFile, NO);
+}
+
+- (void)testReflexiveMetadataEquality {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata0 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ FIRStorageMetadata *metadata1 = metadata0;
+ XCTAssertEqual(metadata0, metadata1);
+ XCTAssertEqualObjects(metadata0, metadata1);
+}
+
+- (void)testNonsenseMetadataEquality {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata0 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotEqualObjects(metadata0, @"I'm not object metadata!");
+}
+
+- (void)testMetadataEquality {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata0 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ FIRStorageMetadata *metadata1 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotEqual(metadata0, metadata1);
+ XCTAssertEqualObjects(metadata0, metadata1);
+}
+
+- (void)testMetadataCopy {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata0 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ FIRStorageMetadata *metadata1 = [metadata0 copy];
+ XCTAssertNotEqual(metadata0, metadata1);
+ XCTAssertEqualObjects(metadata0, metadata1);
+}
+
+- (void)testMetadataHashEquality {
+ NSDictionary *metaDict = @{
+ kFIRStorageMetadataBucket : @"bucket",
+ kFIRStorageMetadataName : @"path/to/object",
+ };
+ FIRStorageMetadata *metadata0 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ FIRStorageMetadata *metadata1 = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotEqual(metadata0, metadata1);
+ XCTAssertEqual([metadata0 hash], [metadata1 hash]);
+}
+
+- (void)testZuluTimeOffset {
+ NSDictionary *metaDict = @{ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108Z" };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata.timeCreated);
+}
+
+- (void)testZuluZeroTimeOffset {
+ NSDictionary *metaDict = @{ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108+0000" };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata.timeCreated);
+}
+
+- (void)testGoogleStandardTimeOffset {
+ NSDictionary *metaDict = @{ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108-0700" };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata.timeCreated);
+}
+
+- (void)testUnspecifiedTimeOffset {
+ NSDictionary *metaDict = @{ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108-0000" };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNotNil(metadata.timeCreated);
+}
+
+- (void)testNoTimeOffset {
+ NSDictionary *metaDict = @{ kFIRStorageMetadataTimeCreated : @"1992-08-07T17:22:53.108" };
+ FIRStorageMetadata *metadata = [[FIRStorageMetadata alloc] initWithDictionary:metaDict];
+ XCTAssertNil(metadata.timeCreated);
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStoragePathTests.m b/Example/Storage/Tests/Unit/FIRStoragePathTests.m
new file mode 100644
index 0000000..017f41d
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStoragePathTests.m
@@ -0,0 +1,234 @@
+// Copyright 2017 Google
+//
+// 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 <XCTest/XCTest.h>
+
+#import "FIRStoragePath.h"
+
+@interface FIRStoragePathTests : XCTestCase
+
+@end
+
+@implementation FIRStoragePathTests
+
+- (void)testGSURI {
+ FIRStoragePath *path = [FIRStoragePath pathFromString:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertEqualObjects(path.object, @"path/to/object");
+}
+
+- (void)testHTTPURL {
+ NSString *httpURL =
+ @"http://firebasestorage.googleapis.com/v0/b/bucket/o/path/to/object?token=signed_url_params";
+ FIRStoragePath *path = [FIRStoragePath pathFromString:httpURL];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertEqualObjects(path.object, @"path/to/object");
+}
+
+- (void)testGSURINoPath {
+ FIRStoragePath *path = [FIRStoragePath pathFromString:@"gs://bucket/"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertNil(path.object);
+}
+
+- (void)testHTTPURLNoPath {
+ FIRStoragePath *path =
+ [FIRStoragePath pathFromString:@"http://firebasestorage.googleapis.com/v0/b/bucket/"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertNil(path.object);
+}
+
+- (void)testGSURINoTrailingSlash {
+ FIRStoragePath *path = [FIRStoragePath pathFromString:@"gs://bucket"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertNil(path.object);
+}
+
+- (void)testHTTPURLNoTrailingSlash {
+ FIRStoragePath *path =
+ [FIRStoragePath pathFromString:@"http://firebasestorage.googleapis.com/v0/b/bucket"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertNil(path.object);
+}
+
+- (void)testGSURIPercentEncoding {
+ FIRStoragePath *path = [FIRStoragePath pathFromString:@"gs://bucket/?/%/#"];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertEqualObjects(path.object, @"?/%/#");
+}
+
+- (void)testHTTPURLPercentEncoding {
+ NSString *httpURL =
+ @"http://firebasestorage.googleapis.com/v0/b/bucket/o/%3F/%25/%23?token=signed_url_params";
+ FIRStoragePath *path = [FIRStoragePath pathFromString:httpURL];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertEqualObjects(path.object, @"?/%/#");
+}
+
+- (void)testHTTPURLNoToken {
+ NSString *httpURL = @"http://firebasestorage.googleapis.com/v0/b/bucket/o/%23hashtag/no/token";
+ FIRStoragePath *path = [FIRStoragePath pathFromString:httpURL];
+ XCTAssertEqualObjects(path.bucket, @"bucket");
+ XCTAssertEqualObjects(path.object, @"#hashtag/no/token");
+}
+
+- (void)testGSURIThrowsOnNoBucket {
+ XCTAssertThrows([FIRStoragePath pathFromString:@"gs://"]);
+}
+
+- (void)testHTTPURLThrowsOnNoBucket {
+ XCTAssertThrows([FIRStoragePath pathFromString:@"http://firebasestorage.googleapis.com/"]);
+}
+
+- (void)testThrowsOnInvalidScheme {
+ NSString *ftpURL = @"ftp://firebasestorage.googleapis.com/v0/b/bucket/o/path/to/object";
+ XCTAssertThrows([FIRStoragePath pathFromString:ftpURL]);
+}
+
+- (void)testHTTPURLNilIncorrectHost {
+ NSString *httpURL = @"http://foo.google.com/v0/b/bucket/o/%3F/%25/%23?token=signed_url_params";
+ XCTAssertThrows([FIRStoragePath pathFromString:httpURL]);
+}
+
+- (void)testchildToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"object"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/object");
+}
+
+- (void)testChildByAppendingNilToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:nil];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/");
+}
+
+- (void)testChildByAppendingNoPathToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@""];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/");
+}
+
+- (void)testChildByAppendingLeadingSlashChildToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"/object"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/object");
+}
+
+- (void)testChildByAppendingTrailingSlashChildToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"object/"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/object");
+}
+
+- (void)testChildByAppendingLeadingAndTrailingSlashChildToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"/object/"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/object");
+}
+
+- (void)testChildByAppendingMultipleChildrenToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"path/to/object"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testChildByAppendingMultipleChildrenWithMultipleSlashesToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"/path//to///object////"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testChildByAppendingOnlySlashesToRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *childPath = [path child:@"//////////"];
+ XCTAssertEqualObjects([childPath stringValue], @"gs://bucket/");
+}
+
+- (void)testParentAtRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *parent = [path parent];
+ XCTAssertNil(parent);
+}
+
+- (void)testParentChildPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ FIRStoragePath *parent = [path parent];
+ XCTAssertEqualObjects([parent stringValue], @"gs://bucket/path/to");
+}
+
+- (void)testParentChildPathSlashes {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"/path//to///"];
+ FIRStoragePath *parent = [path parent];
+ XCTAssertEqualObjects([parent stringValue], @"gs://bucket/path");
+}
+
+- (void)testParentChildPathOnlySlashs {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"/////"];
+ FIRStoragePath *parent = [path parent];
+ XCTAssertNil(parent);
+}
+
+- (void)testRootAtRoot {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *root = [path root];
+ XCTAssertEqualObjects([root stringValue], @"gs://bucket/");
+}
+
+- (void)testRootAtChildPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ FIRStoragePath *root = [path root];
+ XCTAssertEqualObjects([root stringValue], @"gs://bucket/");
+}
+
+- (void)testRootAtSlashPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"//////////"];
+ FIRStoragePath *root = [path root];
+ XCTAssertEqualObjects([root stringValue], @"gs://bucket/");
+}
+
+- (void)testCopy {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"object"];
+ FIRStoragePath *copiedPath = [path copy];
+ XCTAssertNotEqual(copiedPath, path);
+ XCTAssertEqualObjects(copiedPath, path);
+}
+
+- (void)testCopyNoBucket {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:nil object:@"object"];
+#pragma clang diagnostic pop
+ FIRStoragePath *copiedPath = [path copy];
+ XCTAssertNotEqual(copiedPath, path);
+ XCTAssertEqualObjects(copiedPath, path);
+}
+
+- (void)testCopyNoObject {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStoragePath *copiedPath = [path copy];
+ XCTAssertNotEqual(copiedPath, path);
+ XCTAssertEqualObjects(copiedPath, path);
+}
+
+- (void)testCopyNothing {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnonnull"
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:nil object:nil];
+#pragma clang diagnostic pop
+ FIRStoragePath *copiedPath = [path copy];
+ XCTAssertNotEqual(copiedPath, path);
+ XCTAssertEqualObjects(copiedPath, path);
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageReferenceTests.m b/Example/Storage/Tests/Unit/FIRStorageReferenceTests.m
new file mode 100644
index 0000000..e54896c
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageReferenceTests.m
@@ -0,0 +1,163 @@
+// Copyright 2017 Google
+//
+// 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 "FirebaseStorage.h"
+
+#import "FIRStorageReference_Private.h"
+#import "FIRStorageTestHelpers.h"
+
+@interface FIRStorageReferenceTests : XCTestCase
+
+@property(strong, nonatomic) FIRStorage *storage;
+
+@end
+
+@implementation FIRStorageReferenceTests
+
+- (void)setUp {
+ [super setUp];
+
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket");
+
+ id mockApp = OCMClassMock([FIRApp class]);
+ OCMStub([mockApp name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)mockApp options]).andReturn(mockOptions);
+ self.storage = [FIRStorage storageForApp:mockApp];
+}
+
+- (void)tearDown {
+ self.storage = nil;
+ [super tearDown];
+}
+
+- (void)testRoot {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects([ref.root stringValue], @"gs://bucket/");
+}
+
+- (void)testRootWithNoPath {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ XCTAssertEqualObjects([ref.root stringValue], @"gs://bucket/");
+}
+
+- (void)testSingleChild {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *childRef = [ref child:@"path"];
+ XCTAssertEqualObjects([childRef stringValue], @"gs://bucket/path");
+}
+
+- (void)testMultipleChildrenSingleString {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *childRef = [ref child:@"path/to/object"];
+ XCTAssertEqualObjects([childRef stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testMultipleChildrenMultipleStrings {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *childRef = [ref child:@"path"];
+ childRef = [childRef child:@"to"];
+ childRef = [childRef child:@"object"];
+ XCTAssertEqualObjects([childRef stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testSameChildDifferentRef {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *firstRef = [ref child:@"1"];
+ FIRStorageReference *secondRef = [ref child:@"1"];
+ XCTAssertEqualObjects([ref stringValue], @"gs://bucket/");
+ XCTAssertEqualObjects(firstRef, secondRef);
+ XCTAssertNotEqual(firstRef, secondRef);
+}
+
+- (void)testDifferentChildDifferentRef {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *firstRef = [ref child:@"1"];
+ FIRStorageReference *secondRef = [ref child:@"2"];
+ XCTAssertEqualObjects([ref stringValue], @"gs://bucket/");
+ XCTAssertNotEqual(firstRef, secondRef);
+}
+
+- (void)testChildWithTrailingSlash {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object/"];
+ XCTAssertEqualObjects([ref stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testChildWithLeadingSlash {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket//path/to/object/"];
+ XCTAssertEqualObjects([ref stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testChildCompressSlashes {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket//path///to////object////"];
+ XCTAssertEqualObjects([ref stringValue], @"gs://bucket/path/to/object");
+}
+
+- (void)testParent {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object"];
+ FIRStorageReference *parentRef = [ref parent];
+ XCTAssertEqualObjects([parentRef stringValue], @"gs://bucket/path/to");
+}
+
+- (void)testParentToRoot {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path"];
+ FIRStorageReference *parentRef = [ref parent];
+ XCTAssertEqualObjects([parentRef stringValue], @"gs://bucket/");
+}
+
+- (void)testParentToRootTrailingSlash {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/"];
+ FIRStorageReference *parentRef = [ref parent];
+ XCTAssertEqualObjects([parentRef stringValue], @"gs://bucket/");
+}
+
+- (void)testParentAtRoot {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *parentRef = [ref parent];
+ XCTAssertNil(parentRef);
+}
+
+- (void)testBucket {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects(ref.bucket, @"bucket");
+}
+
+- (void)testName {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects(ref.name, @"object");
+}
+
+- (void)testNameNoObject {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ XCTAssertEqualObjects(ref.name, @"");
+}
+
+- (void)testFullPath {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects(ref.fullPath, @"path/to/object");
+}
+
+- (void)testFullPathNoObject {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ XCTAssertEqualObjects(ref.fullPath, @"");
+}
+
+- (void)testCopy {
+ FIRStorageReference *ref = [self.storage referenceForURL:@"gs://bucket/"];
+ FIRStorageReference *copiedRef = [ref copy];
+ XCTAssertEqualObjects(ref, copiedRef);
+ XCTAssertNotEqual(ref, copiedRef);
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageTestHelpers.h b/Example/Storage/Tests/Unit/FIRStorageTestHelpers.h
new file mode 100644
index 0000000..4489c07
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageTestHelpers.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2017 Google
+ *
+ * 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 "FIRAppInternal.h"
+
+#import <Foundation/Foundation.h>
+#import <OCMock/OCMock.h>
+#import <XCTest/XCTest.h>
+
+#import "FIRApp.h"
+#import "FIROptions.h"
+
+#import "FIRStorageConstants.h"
+#import "FIRStorageConstants_Private.h"
+#import "FIRStorageErrors.h"
+#import "FIRStorageMetadata.h"
+#import "FIRStorageReference.h"
+#import "FIRStorageReference_Private.h"
+#import "FIRStorageTask.h"
+#import "FIRStorageTask_Private.h"
+#import "FIRStorageTokenAuthorizer.h"
+#import "FIRStorageUtils.h"
+
+#import <GTMSessionFetcher/GTMSessionFetcher.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+FOUNDATION_EXPORT NSString *const kGoogleHTTPErrorDomain;
+FOUNDATION_EXPORT NSString *const kHTTPVersion;
+FOUNDATION_EXPORT NSString *const kUnauthenticatedResponseString;
+FOUNDATION_EXPORT NSString *const kUnauthorizedResponseString;
+FOUNDATION_EXPORT NSString *const kNotFoundResponseString;
+FOUNDATION_EXPORT NSString *const kInvalidJSONResponseString;
+FOUNDATION_EXPORT NSString *const kFIRStorageValidURL;
+FOUNDATION_EXPORT NSString *const kFIRStorageNotFoundURL;
+FOUNDATION_EXPORT NSString *const kFIRStorageTestAuthToken;
+FOUNDATION_EXPORT NSString *const kFIRStorageAppName;
+
+/**
+ * Standard timeout for all async tests.
+ */
+static NSTimeInterval kExpectationTimeoutSeconds = 10;
+
+@interface FIRStorageTestHelpers : NSObject
+
+/**
+ * Returns a valid URL for an object stored.
+ */
++ (NSURL *)objectURL;
+
+/**
+ * Returns a valid URL for a bucket.
+ */
++ (NSURL *)bucketURL;
+
+/**
+ * Returns a valid URL for an object not found in the current storage bucket.
+ */
++ (NSURL *)notFoundURL;
+
+/**
+ * Returns a valid FIRStoragePath for an object stored.
+ */
++ (FIRStoragePath *)objectPath;
+
+/**
+ * Returns a valid FIRStoragePath for a bucket (no object).
+ */
++ (FIRStoragePath *)bucketPath;
+
+/**
+ * Returns a valid FIRStoragePath for an object not found in the current storage bucket.
+ */
++ (FIRStoragePath *)notFoundPath;
+
+/**
+ * Returns a successful response block.
+ */
++ (GTMSessionFetcherTestBlock)successBlock;
+
+/**
+ * Returns a successful response block containing object metadata.
+ * @param metadata Metadata returned in the request.
+ */
++ (GTMSessionFetcherTestBlock)successBlockWithMetadata:(nullable FIRStorageMetadata *)metadata;
+
+/**
+ * Returns a unsuccessful response block due to improper authentication.
+ */
++ (GTMSessionFetcherTestBlock)unauthenticatedBlock;
+
+/**
+ * Returns a unsuccessful response block due to improper authorization.
+ */
++ (GTMSessionFetcherTestBlock)unauthorizedBlock;
+
+/**
+ * Returns a unsuccessful response block due the object not being found.
+ */
++ (GTMSessionFetcherTestBlock)notFoundBlock;
+
+/**
+ * Returns a unsuccessful response block due invalid JSON returned by the server.
+ */
++ (GTMSessionFetcherTestBlock)invalidJSONBlock;
+
+/**
+ * Waits for the given test case to time out by wrapping -waitForExpectation.
+ */
++ (void)waitForExpectation:(id)test;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Example/Storage/Tests/Unit/FIRStorageTestHelpers.m b/Example/Storage/Tests/Unit/FIRStorageTestHelpers.m
new file mode 100644
index 0000000..fef67ef
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageTestHelpers.m
@@ -0,0 +1,128 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageTestHelpers.h"
+
+NSString *const kGoogleHTTPErrorDomain = @"com.google.HTTPStatus";
+NSString *const kHTTPVersion = @"HTTP/1.1";
+NSString *const kUnauthenticatedResponseString =
+ @"<html><body><p>User not authenticated. Authentication via Authorization header required. "
+ @"Authorization Header does not match expected format of 'Authorization: Firebase "
+ @"<JWT>'.</p></body></html>";
+NSString *const kUnauthorizedResponseString =
+ @"<html><body><p>User not authorized. Authentication via Authorization header required. "
+ @"Authorization Header does not match expected format of 'Authorization: Firebase "
+ @"<JWT>'.</p></body></html>";
+NSString *const kNotFoundResponseString = @"<html><body><p>Object not found.</p></body></html>";
+NSString *const kInvalidJSONResponseString = @"This is not a JSON object";
+NSString *const kFIRStorageObjectURL =
+ @"https://firebasestorage.googleapis.com/v0/b/bucket/o/object";
+NSString *const kFIRStorageBucketURL = @"https://firebasestorage.googleapis.com/v0/b/bucket/o";
+NSString *const kFIRStorageNotFoundURL =
+ @"https://firebasestorage.googleapis.com/v0/b/bucket/o/i/dont/exist";
+NSString *const kFIRStorageTestAuthToken = @"1234-5678-9012-3456-7890";
+NSString *const kFIRStorageAppName = @"app";
+
+@implementation FIRStorageTestHelpers
+
++ (NSURL *)objectURL {
+ return [NSURL URLWithString:kFIRStorageObjectURL];
+}
+
++ (NSURL *)bucketURL {
+ return [NSURL URLWithString:kFIRStorageBucketURL];
+}
+
++ (NSURL *)notFoundURL {
+ return [NSURL URLWithString:kFIRStorageNotFoundURL];
+}
+
++ (FIRStoragePath *)objectPath {
+ return [FIRStoragePath pathFromString:kFIRStorageObjectURL];
+}
+
++ (FIRStoragePath *)bucketPath {
+ return [FIRStoragePath pathFromString:kFIRStorageBucketURL];
+}
+
++ (FIRStoragePath *)notFoundPath {
+ return [FIRStoragePath pathFromString:kFIRStorageNotFoundURL];
+}
+
++ (GTMSessionFetcherTestBlock)successBlock {
+ return [FIRStorageTestHelpers successBlockWithMetadata:nil];
+}
+
++ (GTMSessionFetcherTestBlock)successBlockWithMetadata:(nullable FIRStorageMetadata *)metadata {
+ NSData *data;
+ if (metadata) {
+ data = [NSData frs_dataFromJSONDictionary:[metadata dictionaryRepresentation]];
+ }
+ return [FIRStorageTestHelpers blockForData:data statusCode:200];
+}
+
++ (GTMSessionFetcherTestBlock)unauthenticatedBlock {
+ NSData *data = [kUnauthenticatedResponseString dataUsingEncoding:NSUTF8StringEncoding];
+ return [FIRStorageTestHelpers blockForData:data statusCode:401];
+}
+
++ (GTMSessionFetcherTestBlock)unauthorizedBlock {
+ NSData *data = [kUnauthorizedResponseString dataUsingEncoding:NSUTF8StringEncoding];
+ return [FIRStorageTestHelpers blockForData:data statusCode:403];
+}
+
++ (GTMSessionFetcherTestBlock)notFoundBlock {
+ NSData *data = [kNotFoundResponseString dataUsingEncoding:NSUTF8StringEncoding];
+ return [FIRStorageTestHelpers blockForData:data statusCode:404];
+}
+
++ (GTMSessionFetcherTestBlock)invalidJSONBlock {
+ NSData *data = [kInvalidJSONResponseString dataUsingEncoding:NSUTF8StringEncoding];
+ return [FIRStorageTestHelpers blockForData:data statusCode:200];
+}
+
+#pragma mark - Private methods
+
++ (GTMSessionFetcherTestBlock)blockForData:(nullable NSData *)data statusCode:(NSInteger)code {
+ GTMSessionFetcherTestBlock block =
+ ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:code
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ NSError *error;
+ if (code >= 400) {
+ NSDictionary *userInfo;
+ if (data) {
+ userInfo = @{ @"data" : data };
+ }
+ error = [NSError errorWithDomain:kGoogleHTTPErrorDomain code:code userInfo:userInfo];
+ }
+
+ response(httpResponse, data, error);
+ };
+ return block;
+}
+
++ (void)waitForExpectation:(id)test {
+ [test waitForExpectationsWithTimeout:kExpectationTimeoutSeconds
+ handler:^(NSError *_Nullable error) {
+ if (error) {
+ NSLog(@"Error: %@", error);
+ }
+ }];
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageTests.m b/Example/Storage/Tests/Unit/FIRStorageTests.m
new file mode 100644
index 0000000..1b45295
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageTests.m
@@ -0,0 +1,214 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageTestHelpers.h"
+
+#import "FIRStorageReference.h"
+#import "FIRStorageReference_Private.h"
+#import "FIRStorage_Private.h"
+
+@interface FIRStorageTests : XCTestCase
+
+@property(strong, nonatomic) id app;
+
+@end
+
+@implementation FIRStorageTests
+
+- (void)setUp {
+ [super setUp];
+
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket");
+
+ self.app = OCMClassMock([FIRApp class]);
+ OCMStub([self.app name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)self.app options]).andReturn(mockOptions);
+}
+
+- (void)tearDown {
+ self.app = nil;
+ [super tearDown];
+}
+
+- (void)testBucketNotEnforced {
+ FIROptions * mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"");
+ FIRApp *app = OCMClassMock([FIRApp class]);
+ OCMStub([app name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)app options]).andReturn(mockOptions);
+
+ FIRStorage *storage = [FIRStorage storageForApp:app];
+ [storage referenceForURL:@"gs://benwu-test1.storage.firebase.com/child"];
+ [storage referenceForURL:@"gs://benwu-test2.storage.firebase.com/child"];
+}
+
+- (void)testBucketEnforced {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app
+ URL:@"gs://benwu-test1.storage.firebase.com"];
+ [storage referenceForURL:@"gs://benwu-test1.storage.firebase.com/child"];
+ storage = [FIRStorage storageForApp:self.app URL:@"gs://benwu-test1.storage.firebase.com/"];
+ [storage referenceForURL:@"gs://benwu-test1.storage.firebase.com/child"];
+ XCTAssertThrows([storage referenceForURL:@"gs://benwu-test2.storage.firebase.com/child"]);
+}
+
+- (void) testInitWithCustomUrl {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app URL:@"gs://foo-bar.appspot.com"];
+ XCTAssertEqualObjects(@"gs://foo-bar.appspot.com/", [[storage reference] description]);
+ storage = [FIRStorage storageForApp:self.app URL:@"gs://foo-bar.appspot.com/"];
+ XCTAssertEqualObjects(@"gs://foo-bar.appspot.com/", [[storage reference] description]);
+}
+
+- (void) testInitWithWrongScheme {
+ XCTAssertThrows([FIRStorage storageForApp:self.app URL:@"http://foo-bar.appspot.com"]);
+}
+
+- (void) testInitWithNoScheme {
+ XCTAssertThrows([FIRStorage storageForApp:self.app URL:@"foo-bar.appspot.com"]);
+}
+
+- (void) testInitWithNilURL {
+ XCTAssertThrows([FIRStorage storageForApp:self.app URL:nil]);
+}
+
+- (void) testInitWithPath {
+ XCTAssertThrows([FIRStorage storageForApp:self.app URL:@"gs://foo-bar.appspot.com/child"]);
+}
+
+- (void) testInitWithDefaultAndCustomUrl {
+ FIRStorage *customInstance = [FIRStorage storageForApp:self.app
+ URL:@"gs://foo-bar.appspot.com"];
+ FIRStorage *defaultInstance = [FIRStorage storageForApp:self.app];
+ XCTAssertEqualObjects(@"gs://foo-bar.appspot.com/", [[customInstance reference] description]);
+ XCTAssertEqualObjects(@"gs://bucket/", [[defaultInstance reference] description]);
+}
+
+- (void)testStorageDefaultApp {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app];
+ XCTAssertEqualObjects(storage.app.name, ((FIRApp *)self.app).name);
+ XCTAssertNotNil(storage.fetcherServiceForApp);
+}
+
+- (void)testStorageCustomApp {
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket");
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ FIRStorage *storage = [FIRStorage storageForApp:secondApp];
+ XCTAssertNotEqual(storage.app.name, ((FIRApp *)self.app).name);
+ XCTAssertNotNil(storage.fetcherServiceForApp);
+ XCTAssertNotEqualObjects(storage.fetcherServiceForApp,
+ [FIRStorage storageForApp:self.app].fetcherServiceForApp);
+}
+
+- (void)testStorageNoBucketInConfig {
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(nil);
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ XCTAssertThrows([FIRStorage storageForApp:secondApp]);
+}
+
+- (void)testStorageEmptyBucketInConfig {
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"");
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ FIRStorage *storage = [FIRStorage storageForApp:secondApp];
+ FIRStorageReference *storageRef = [storage referenceForURL:@"gs://bucket/path/to/object"];
+ XCTAssertEqualObjects(storageRef.bucket, @"bucket");
+}
+
+- (void)testStorageWrongBucketInConfig {
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"notMyBucket");
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ FIRStorage *storage = [FIRStorage storageForApp:secondApp];
+ XCTAssertEqualObjects([(FIRApp *)secondApp options].storageBucket, @"notMyBucket");
+ XCTAssertThrows([storage referenceForURL:@"gs://bucket/path/to/object"]);
+}
+
+- (void)testRefDefaultApp {
+ FIRStorageReference *convenienceRef =
+ [[FIRStorage storageForApp:self.app] referenceForURL:@"gs://bucket/path/to/object"];
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ FIRStorageReference *builtRef =
+ [[FIRStorageReference alloc] initWithStorage:[FIRStorage storageForApp:self.app] path:path];
+ XCTAssertEqualObjects([convenienceRef description], [builtRef description]);
+ XCTAssertEqualObjects(convenienceRef.storage.app, builtRef.storage.app);
+}
+
+- (void)testRefCustomApp {
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket");
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ FIRStorageReference *convenienceRef =
+ [[FIRStorage storageForApp:secondApp] referenceForURL:@"gs://bucket/path/to/object"];
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ FIRStorageReference *builtRef =
+ [[FIRStorageReference alloc] initWithStorage:[FIRStorage storageForApp:secondApp] path:path];
+ XCTAssertEqualObjects([convenienceRef description], [builtRef description]);
+ XCTAssertEqualObjects(convenienceRef.storage.app, builtRef.storage.app);
+}
+
+- (void)testRootRefDefaultApp {
+ FIRStorageReference *convenienceRef = [[FIRStorage storageForApp:self.app] reference];
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ FIRStorageReference *builtRef =
+ [[FIRStorageReference alloc] initWithStorage:[FIRStorage storageForApp:self.app] path:path];
+ XCTAssertEqualObjects([convenienceRef description], [builtRef description]);
+ XCTAssertEqualObjects(convenienceRef.storage.app, builtRef.storage.app);
+}
+
+- (void)testRefWithPathDefaultApp {
+ FIRStorageReference *convenienceRef =
+ [[FIRStorage storageForApp:self.app] referenceWithPath:@"path/to/object"];
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ FIRStorageReference *builtRef =
+ [[FIRStorageReference alloc] initWithStorage:[FIRStorage storageForApp:self.app] path:path];
+ XCTAssertEqualObjects([convenienceRef description], [builtRef description]);
+ XCTAssertEqualObjects(convenienceRef.storage.app, builtRef.storage.app);
+}
+
+- (void)testEqual {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app];
+ FIRStorage *copy = [storage copy];
+ XCTAssertEqualObjects(storage.app.name, copy.app.name);
+}
+
+- (void)testNotEqual {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app];
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket");
+ id secondApp = OCMClassMock([FIRApp class]);
+ OCMStub([secondApp name]).andReturn(@"secondApp");
+ OCMStub([(FIRApp *)secondApp options]).andReturn(mockOptions);
+ FIRStorage *secondStorage = [FIRStorage storageForApp:secondApp];
+ XCTAssertNotEqualObjects(storage, secondStorage);
+}
+
+- (void)testHash {
+ FIRStorage *storage = [FIRStorage storageForApp:self.app];
+ FIRStorage *copy = [storage copy];
+ XCTAssertEqual([storage hash], [copy hash]);
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageTokenAuthorizerTests.m b/Example/Storage/Tests/Unit/FIRStorageTokenAuthorizerTests.m
new file mode 100644
index 0000000..e98ffbb
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageTokenAuthorizerTests.m
@@ -0,0 +1,226 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageTestHelpers.h"
+
+@interface FIRStorageTokenAuthorizerTests : XCTestCase
+
+@property(strong, nonatomic) GTMSessionFetcher *fetcher;
+@property(strong, nonatomic) id mockApp;
+
+@end
+
+@implementation FIRStorageTokenAuthorizerTests
+
+- (void)setUp {
+ [super setUp];
+ NSURLRequest *fetchRequest = [NSURLRequest requestWithURL:[FIRStorageTestHelpers objectURL]];
+ self.fetcher = [GTMSessionFetcher fetcherWithRequest:fetchRequest];
+
+ self.mockApp = OCMClassMock([FIRApp class]);
+ OCMStub([self.mockApp getTokenImplementation])
+ .andReturn(^{
+ });
+ FIRTokenCallback mockCallback =
+ [OCMArg invokeBlockWithArgs:kFIRStorageTestAuthToken, [NSNull null], nil];
+ OCMStub([self.mockApp getTokenForcingRefresh:NO withCallback:mockCallback]);
+ GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcher.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:self.mockApp fetcherService:fetcherService];
+}
+
+- (void)tearDown {
+ self.fetcher = nil;
+ self.mockApp = nil;
+ [super tearDown];
+}
+
+- (void)testSuccessfulAuth {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulAuth"];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertTrue([self.fetcher.authorizer isAuthorizedRequest:fetcher.request]);
+#pragma clang diagnostic pop
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ [self.fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data,
+ NSError *_Nullable error) {
+ NSDictionary<NSString *, NSString *> *headers = self.fetcher.request.allHTTPHeaderFields;
+ NSString *authHeader = [headers objectForKey:@"Authorization"];
+ NSString *firebaseToken =
+ [NSString stringWithFormat:kFIRStorageAuthTokenFormat, kFIRStorageTestAuthToken];
+ XCTAssertEqualObjects(authHeader, firebaseToken);
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulAuth {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testUnsuccessfulAuth"];
+
+ NSError *authError = [NSError errorWithDomain:FIRStorageErrorDomain
+ code:FIRStorageErrorCodeUnauthenticated
+ userInfo:nil];
+ id unsuccessfulApp = OCMClassMock([FIRApp class]);
+ OCMStub([unsuccessfulApp getTokenImplementation])
+ .andReturn(^{
+ });
+ FIRTokenCallback mockCallback = [OCMArg invokeBlockWithArgs:[NSNull null], authError, nil];
+ OCMStub([unsuccessfulApp getTokenForcingRefresh:NO withCallback:mockCallback]);
+ GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcher.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:unsuccessfulApp fetcherService:fetcherService];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertEqual([self.fetcher.authorizer isAuthorizedRequest:fetcher.request], NO);
+#pragma cland diagnostic pop
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:401
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, authError);
+ };
+
+ [self.fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data,
+ NSError *_Nullable error) {
+ NSDictionary<NSString *, NSString *> *headers = self.fetcher.request.allHTTPHeaderFields;
+ NSString *authHeader = [headers objectForKey:@"Authorization"];
+ XCTAssertNil(authHeader);
+ XCTAssertEqualObjects(error.domain, FIRStorageErrorDomain);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnauthenticated);
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testSuccessfulUnauthenticatedAuth {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testSuccessfulUnauthenticatedAuth"];
+
+ // Note that self.mockApp is left with null properties--this simulates no token present
+ self.mockApp = OCMClassMock([FIRApp class]);
+ GTMSessionFetcherService *fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcher.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:self.mockApp fetcherService:fetcherService];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertFalse([self.fetcher.authorizer isAuthorizedRequest:fetcher.request]);
+#pragma cland diagnostic pop
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ [self.fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data,
+ NSError *_Nullable error) {
+ NSDictionary<NSString *, NSString *> *headers = self.fetcher.request.allHTTPHeaderFields;
+ NSString *authHeader = [headers objectForKey:@"Authorization"];
+ XCTAssertNil(authHeader);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testIsAuthorizing {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testIsAuthorizing"];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+ XCTAssertFalse([fetcher.authorizer isAuthorizingRequest:fetcher.request]);
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ [self.fetcher
+ beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testStopAuthorizingNoop {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testStopAuthorizingNoop"];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+ // Since both of these are noops, we expect that invoking them
+ // will still result in successful authentication
+ [fetcher.authorizer stopAuthorization];
+ [fetcher.authorizer stopAuthorizationForRequest:fetcher.request];
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ [self.fetcher beginFetchWithCompletionHandler:^(NSData *_Nullable data,
+ NSError *_Nullable error) {
+ NSDictionary<NSString *, NSString *> *headers = self.fetcher.request.allHTTPHeaderFields;
+ NSString *authHeader = [headers objectForKey:@"Authorization"];
+ NSString *firebaseToken =
+ [NSString stringWithFormat:kFIRStorageAuthTokenFormat, kFIRStorageTestAuthToken];
+ XCTAssertEqualObjects(authHeader, firebaseToken);
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testEmail {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testEmail"];
+
+ self.fetcher.testBlock = ^(GTMSessionFetcher *fetcher, GTMSessionFetcherTestResponse response) {
+ XCTAssertNil([fetcher.authorizer userEmail]);
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ [self.fetcher
+ beginFetchWithCompletionHandler:^(NSData *_Nullable data, NSError *_Nullable error) {
+ [expectation fulfill];
+ }];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageUpdateMetadataTests.m b/Example/Storage/Tests/Unit/FIRStorageUpdateMetadataTests.m
new file mode 100644
index 0000000..a85d181
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageUpdateMetadataTests.m
@@ -0,0 +1,199 @@
+// Copyright 2017 Google
+//
+// 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 "FIRStorageTestHelpers.h"
+#import "FIRStorageUpdateMetadataTask.h"
+
+@interface FIRStorageUpdateMetadataTests : XCTestCase
+
+@property(strong, nonatomic) GTMSessionFetcherService *fetcherService;
+@property(strong, nonatomic) FIRStorageMetadata *metadata;
+@property(strong, nonatomic) FIRStorage *storage;
+@property(strong, nonatomic) id mockApp;
+
+@end
+
+@implementation FIRStorageUpdateMetadataTests
+
+- (void)setUp {
+ [super setUp];
+
+ NSDictionary *metadataDict = @{ @"bucket" : @"bucket", @"name" : @"path/to/object" };
+ self.metadata = [[FIRStorageMetadata alloc] initWithDictionary:metadataDict];
+
+ id mockOptions = OCMClassMock([FIROptions class]);
+ OCMStub([mockOptions storageBucket]).andReturn(@"bucket.appspot.com");
+
+ self.mockApp = OCMClassMock([FIRApp class]);
+ OCMStub([self.mockApp name]).andReturn(kFIRStorageAppName);
+ OCMStub([(FIRApp *)self.mockApp options]).andReturn(mockOptions);
+
+ self.fetcherService = [[GTMSessionFetcherService alloc] init];
+ self.fetcherService.authorizer =
+ [[FIRStorageTokenAuthorizer alloc] initWithApp:self.mockApp
+ fetcherService:self.fetcherService];
+
+ self.storage = [FIRStorage storageForApp:self.mockApp];
+}
+
+- (void)tearDown {
+ self.fetcherService = nil;
+ self.storage = nil;
+ self.mockApp = nil;
+ [super tearDown];
+}
+
+- (void)testFetcherConfiguration {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock = ^(GTMSessionFetcher *fetcher,
+ GTMSessionFetcherTestResponse response) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-retain-cycles"
+ XCTAssertEqualObjects(fetcher.request.URL, [FIRStorageTestHelpers objectURL]);
+ XCTAssertEqualObjects(fetcher.request.HTTPMethod, @"PATCH");
+ NSData *bodyData = [NSData frs_dataFromJSONDictionary:[self.metadata dictionaryRepresentation]];
+ XCTAssertEqualObjects(fetcher.request.HTTPBody, bodyData);
+ NSDictionary *HTTPHeaders = fetcher.request.allHTTPHeaderFields;
+ XCTAssertEqualObjects(HTTPHeaders[@"Content-Type"], @"application/json; charset=UTF-8");
+ XCTAssertEqualObjects(HTTPHeaders[@"Content-Length"], [@(bodyData.length) stringValue]);
+#pragma clang diagnostic pop
+ NSHTTPURLResponse *httpResponse =
+ [[NSHTTPURLResponse alloc] initWithURL:fetcher.request.URL
+ statusCode:200
+ HTTPVersion:kHTTPVersion
+ headerFields:nil];
+ response(httpResponse, nil, nil);
+ };
+
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testSuccessfulFetch {
+ XCTestExpectation *expectation = [self expectationWithDescription:@"testSuccessfulFetch"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers successBlockWithMetadata:self.metadata];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ XCTAssertEqualObjects(self.metadata.bucket, metadata.bucket);
+ XCTAssertEqualObjects(self.metadata.name, metadata.name);
+ XCTAssertNil(error);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthenticated {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthenticated"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthenticatedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ XCTAssertNil(metadata);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnauthenticated);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchUnauthorized {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchUnauthorized"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers unauthorizedBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ XCTAssertNil(metadata);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnauthorized);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchObjectDoesntExist {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchObjectDoesntExist"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers notFoundBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers notFoundPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ XCTAssertNil(metadata);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeObjectNotFound);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+- (void)testUnsuccessfulFetchBadJSON {
+ XCTestExpectation *expectation =
+ [self expectationWithDescription:@"testUnsuccessfulFetchBadJSON"];
+
+ self.fetcherService.testBlock = [FIRStorageTestHelpers invalidJSONBlock];
+ FIRStoragePath *path = [FIRStorageTestHelpers objectPath];
+ FIRStorageReference *ref = [[FIRStorageReference alloc] initWithStorage:self.storage path:path];
+ FIRStorageUpdateMetadataTask *task = [[FIRStorageUpdateMetadataTask alloc]
+ initWithReference:ref
+ fetcherService:self.fetcherService
+ metadata:self.metadata
+ completion:^(FIRStorageMetadata *_Nullable metadata, NSError *_Nullable error) {
+ XCTAssertNil(metadata);
+ XCTAssertEqual(error.code, FIRStorageErrorCodeUnknown);
+ [expectation fulfill];
+ }];
+ [task enqueue];
+
+ [FIRStorageTestHelpers waitForExpectation:self];
+}
+
+@end
diff --git a/Example/Storage/Tests/Unit/FIRStorageUtilsTests.m b/Example/Storage/Tests/Unit/FIRStorageUtilsTests.m
new file mode 100644
index 0000000..8bf4c7e
--- /dev/null
+++ b/Example/Storage/Tests/Unit/FIRStorageUtilsTests.m
@@ -0,0 +1,125 @@
+// Copyright 2017 Google
+//
+// 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 <XCTest/XCTest.h>
+
+#import "FIRStorageUtils.h"
+
+#import "FIRStoragePath.h"
+
+@interface FIRStorageUtilsTests : XCTestCase
+
+@end
+
+@implementation FIRStorageUtilsTests
+
+- (void)testCommonExtensionToMIMEType {
+ NSDictionary<NSString *, NSString *> *extensionToMIMEType = @{
+ @"txt" : @"text/plain",
+ @"png" : @"image/png",
+ @"mp3" : @"audio/mpeg",
+ @"mov" : @"video/quicktime",
+ @"gif" : @"image/gif"
+ };
+ [extensionToMIMEType
+ enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull extension, NSString *_Nonnull MIMEType,
+ BOOL *_Nonnull stop) {
+ XCTAssertEqualObjects([FIRStorageUtils MIMETypeForExtension:extension], MIMEType);
+ }];
+}
+
+- (void)testParseGoodDataToDict {
+ NSString *JSONString = @"{\"hello\" : \"world\"}";
+ NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
+ NSDictionary *JSONDictionary = [NSDictionary frs_dictionaryFromJSONData:JSONData];
+ NSDictionary *expectedDictionary = @{ @"hello" : @"world" };
+ XCTAssertEqualObjects(JSONDictionary, expectedDictionary);
+}
+
+- (void)testParseBadDataToDict {
+ NSString *JSONString = @"Invalid JSON Object";
+ NSData *JSONData = [JSONString dataUsingEncoding:NSUTF8StringEncoding];
+ NSDictionary *JSONDictionary = [NSDictionary frs_dictionaryFromJSONData:JSONData];
+ XCTAssertNil(JSONDictionary);
+}
+
+- (void)testParseNilToDict {
+ NSDictionary *JSONDictionary = [NSDictionary frs_dictionaryFromJSONData:nil];
+ XCTAssertNil(JSONDictionary);
+}
+
+- (void)testParseGoodDictToData {
+ NSDictionary *JSONDictionary = @{ @"hello" : @"world" };
+ NSData *expectedData = [NSData frs_dataFromJSONDictionary:JSONDictionary];
+ NSString *JSONString = [[NSString alloc] initWithData:expectedData encoding:NSUTF8StringEncoding];
+ NSString *expectedString = @"{\"hello\":\"world\"}";
+ XCTAssertEqualObjects(JSONString, expectedString);
+}
+
+- (void)testParseNilToData {
+ NSData *JSONData = [NSData frs_dataFromJSONDictionary:nil];
+ XCTAssertNil(JSONData);
+}
+
+- (void)testNilDictToQueryString {
+ NSDictionary *params;
+ NSString *queryString = [FIRStorageUtils queryStringForDictionary:params];
+ XCTAssertEqualObjects(queryString, @"");
+}
+
+- (void)testEmptyDictToQueryString {
+ NSDictionary *params = @{};
+ NSString *queryString = [FIRStorageUtils queryStringForDictionary:params];
+ XCTAssertEqualObjects(queryString, @"");
+}
+
+- (void)testSingleItemToQueryString {
+ NSDictionary *params = @{ @"foo" : @"bar" };
+ NSString *queryString = [FIRStorageUtils queryStringForDictionary:params];
+ XCTAssertEqualObjects(queryString, @"foo=bar");
+}
+
+- (void)testMultiItemDictToQueryString {
+ NSDictionary *params = @{ @"foo" : @"bar", @"baz" : @"qux" };
+ NSString *queryString = [FIRStorageUtils queryStringForDictionary:params];
+ XCTAssertEqualObjects(queryString, @"foo=bar&baz=qux");
+}
+
+- (void)testDefaultRequestForFullPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ NSURLRequest *request = [FIRStorageUtils defaultRequestForPath:path];
+ XCTAssertEqualObjects([request.URL absoluteString],
+ @"https://firebasestorage.googleapis.com/v0/b/bucket/o/path%2Fto%2Fobject");
+}
+
+- (void)testDefaultRequestForNoPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ NSURLRequest *request = [FIRStorageUtils defaultRequestForPath:path];
+ XCTAssertEqualObjects([request.URL absoluteString],
+ @"https://firebasestorage.googleapis.com/v0/b/bucket/o");
+}
+
+- (void)testEncodedURLForFullPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:@"path/to/object"];
+ NSString *encodedURL = [FIRStorageUtils encodedURLForPath:path];
+ XCTAssertEqualObjects(encodedURL, @"/v0/b/bucket/o/path%2Fto%2Fobject");
+}
+
+- (void)testEncodedURLForNoPath {
+ FIRStoragePath *path = [[FIRStoragePath alloc] initWithBucket:@"bucket" object:nil];
+ NSString *encodedURL = [FIRStorageUtils encodedURLForPath:path];
+ XCTAssertEqualObjects(encodedURL, @"/v0/b/bucket/o");
+}
+
+@end