|
@@ -0,0 +1,226 @@
|
|
|
|
+//
|
|
|
|
+// InAppReceiptVerificatorTests.swift
|
|
|
|
+// SwiftyStoreKit
|
|
|
|
+//
|
|
|
|
+// Created by Andrea Bizzotto on 17/05/2017.
|
|
|
|
+// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com)
|
|
|
|
+//
|
|
|
|
+// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
+// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
+// in the Software without restriction, including without limitation the rights
|
|
|
|
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
+// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
+// furnished to do so, subject to the following conditions:
|
|
|
|
+//
|
|
|
|
+// The above copyright notice and this permission notice shall be included in
|
|
|
|
+// all copies or substantial portions of the Software.
|
|
|
|
+//
|
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
+// THE SOFTWARE.
|
|
|
|
+
|
|
|
|
+import XCTest
|
|
|
|
+@testable import SwiftyStoreKit
|
|
|
|
+
|
|
|
|
+class TestReceiptValidator: ReceiptValidator {
|
|
|
|
+ var validateCalled = false
|
|
|
|
+ func validate(receipt: String, password autoRenewPassword: String?, completion: @escaping (VerifyReceiptResult) -> Void) {
|
|
|
|
+ validateCalled = true
|
|
|
|
+ completion(.success(receipt: [:]))
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class TestInAppReceiptRefreshRequest: InAppReceiptRefreshRequest {
|
|
|
|
+
|
|
|
|
+ override func start() {
|
|
|
|
+ // do nothing
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+extension VerifyReceiptResult: Equatable {
|
|
|
|
+
|
|
|
|
+ static public func == (lhs: VerifyReceiptResult, rhs: VerifyReceiptResult) -> Bool {
|
|
|
|
+ switch (lhs, rhs) {
|
|
|
|
+ case (.success(_), .success(_)): return true
|
|
|
|
+ case (.error(let lhsError), .error(let rhsError)): return lhsError == rhsError
|
|
|
|
+ default: return false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+extension ReceiptError: Equatable {
|
|
|
|
+
|
|
|
|
+ static public func == (lhs: ReceiptError, rhs: ReceiptError) -> Bool {
|
|
|
|
+ switch (lhs, rhs) {
|
|
|
|
+ case (.noReceiptData, .noReceiptData): return true
|
|
|
|
+ case (.noRemoteData, .noRemoteData): return true
|
|
|
|
+ case (.requestBodyEncodeError(_), .requestBodyEncodeError(_)): return true
|
|
|
|
+ case (.networkError(_), .networkError(_)): return true
|
|
|
|
+ case (.jsonDecodeError(_), .jsonDecodeError(_)): return true
|
|
|
|
+ case (.receiptInvalid(_, _), .receiptInvalid(_, _)): return true
|
|
|
|
+ default: return false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class InAppReceiptVerificatorTests: XCTestCase {
|
|
|
|
+
|
|
|
|
+ // MARK: refresh tests (no receipt url or no receipt data)
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNil_then_callsRefresh() {
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
|
|
|
|
+
|
|
|
|
+ var refreshCalled = false
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ refreshCalled = true
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { _ in
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ XCTAssertTrue(refreshCalled)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNotNil_noReceiptData_then_callsRefresh() {
|
|
|
|
+
|
|
|
|
+ let testReceiptURL = makeReceiptURL()
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
|
|
|
|
+
|
|
|
|
+ var refreshCalled = false
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ refreshCalled = true
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { _ in
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ XCTAssertTrue(refreshCalled)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNil_refreshCallbackError_then_errorNetworkError() {
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
|
|
|
|
+ let refreshError = NSError(domain: "", code: 0, userInfo: nil)
|
|
|
|
+
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ callback(.error(e: refreshError))
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { result in
|
|
|
|
+
|
|
|
|
+ XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.networkError(error: refreshError)))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNil_refreshCallbackSuccess_receiptDataNotWritten_then_errorNoReceiptData_validateNotCalled() {
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
|
|
|
|
+
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ callback(.success)
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { result in
|
|
|
|
+
|
|
|
|
+ XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.noReceiptData))
|
|
|
|
+ }
|
|
|
|
+ XCTAssertFalse(validator.validateCalled)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNil_noReceiptData_refreshCallbackSuccess_receiptDataWritten_then_errorNoReceiptData_validateNotCalled() {
|
|
|
|
+
|
|
|
|
+ let testReceiptURL = makeReceiptURL()
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: nil)
|
|
|
|
+
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ writeReceiptData(to: testReceiptURL)
|
|
|
|
+ callback(.success)
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { result in
|
|
|
|
+
|
|
|
|
+ XCTAssertEqual(result, VerifyReceiptResult.error(error: ReceiptError.noReceiptData))
|
|
|
|
+ }
|
|
|
|
+ XCTAssertFalse(validator.validateCalled)
|
|
|
|
+ removeReceiptData(at: testReceiptURL)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNotNil_noReceiptData_refreshCallbackSuccess_receiptDataWritten_then_validateIsCalled() {
|
|
|
|
+
|
|
|
|
+ let testReceiptURL = makeReceiptURL()
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
|
|
|
|
+
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ writeReceiptData(to: testReceiptURL)
|
|
|
|
+ callback(.success)
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { _ in
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ XCTAssertTrue(validator.validateCalled)
|
|
|
|
+ removeReceiptData(at: testReceiptURL)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // MARK: non-refresh tests (receipt url and data are set)
|
|
|
|
+ func testVerifyReceipt_when_appStoreReceiptURLIsNotNil_hasReceiptData_then_refreshNotCalled_validateIsCalled() {
|
|
|
|
+
|
|
|
|
+ let testReceiptURL = makeReceiptURL()
|
|
|
|
+ writeReceiptData(to: testReceiptURL)
|
|
|
|
+
|
|
|
|
+ let validator = TestReceiptValidator()
|
|
|
|
+ let verificator = InAppReceiptVerificator(appStoreReceiptURL: testReceiptURL)
|
|
|
|
+
|
|
|
|
+ verificator.verifyReceipt(using: validator, password: nil, refresh: { (properties, callback) -> InAppReceiptRefreshRequest in
|
|
|
|
+
|
|
|
|
+ XCTFail("refresh should not be called if we already have a receipt")
|
|
|
|
+ return TestInAppReceiptRefreshRequest(receiptProperties: properties, callback: callback)
|
|
|
|
+
|
|
|
|
+ }) { _ in
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ XCTAssertTrue(validator.validateCalled)
|
|
|
|
+ removeReceiptData(at: testReceiptURL)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // MARK: Helpers
|
|
|
|
+ func makeReceiptURL() -> URL {
|
|
|
|
+
|
|
|
|
+ guard let testFolderURL = try? FileManager.default.url(for: .documentDirectory, in: .allDomainsMask, appropriateFor: nil, create: false) else {
|
|
|
|
+ fatalError("Invalid test folder")
|
|
|
|
+ }
|
|
|
|
+ return testFolderURL.appendingPathComponent("receipt.data")
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func writeReceiptData(to url: URL) {
|
|
|
|
+
|
|
|
|
+ guard let testReceiptData = NSData(base64Encoded: "encrypted-receipt", options: .ignoreUnknownCharacters) else {
|
|
|
|
+ fatalError("Invalid receipt data")
|
|
|
|
+ }
|
|
|
|
+ try? testReceiptData.write(to: url)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func removeReceiptData(at url: URL) {
|
|
|
|
+ try? FileManager.default.removeItem(at: url)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|