Rearrange targets for unit testing (#900)

- Move and rename entities
- Split protocols and default extensions
- Move in-app entities to CommonIAP target
This commit is contained in:
Davide 2024-11-20 18:05:47 +01:00 committed by GitHub
parent 2ccd3052ac
commit b45f9c23fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 410 additions and 208 deletions

View File

@ -105,6 +105,34 @@
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "PassepartoutImplementations"
BuildableName = "PassepartoutImplementations"
BlueprintName = "PassepartoutImplementations"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CommonIAP"
BuildableName = "CommonIAP"
BlueprintName = "CommonIAP"
ReferencedContainer = "container:">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction

View File

@ -41,8 +41,7 @@
"kind" : "remoteSourceControl",
"location" : "git@github.com:passepartoutvpn/passepartoutkit-source",
"state" : {
"revision" : "3a4c78af67dfe181acc657a5539ee3d62d1c9361",
"version" : "0.11.0"
"revision" : "d11036e59b65601b617120471dc9a469567388f5"
}
},
{

View File

@ -15,11 +15,15 @@ let package = Package(
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "AppUIMain",
targets: ["AppUIMain"]
targets: ["AppUIMainWrapper"]
),
.library(
name: "AppUITV",
targets: ["AppUITV"]
targets: ["AppUITVWrapper"]
),
.library(
name: "CommonIAP",
targets: ["CommonIAP"]
),
.library(
name: "CommonLibrary",
@ -95,10 +99,22 @@ let package = Package(
.process("Resources")
]
),
.target(
name: "AppUIMainWrapper",
dependencies: [
.target(name: "AppUIMain", condition: .when(platforms: [.iOS, .macOS]))
]
),
.target(
name: "AppUITV",
dependencies: ["UILibrary"]
),
.target(
name: "AppUITVWrapper",
dependencies: [
.target(name: "AppUITV", condition: .when(platforms: [.tvOS]))
]
),
.target(
name: "CommonAPI",
dependencies: ["CommonLibrary"],
@ -106,9 +122,14 @@ let package = Package(
.copy("API")
]
),
.target(
name: "CommonIAP",
dependencies: ["CommonUtils"]
),
.target(
name: "CommonLibrary",
dependencies: [
"CommonIAP",
"CommonUtils",
.product(name: "PassepartoutKit", package: "passepartoutkit-source")
],

View File

@ -27,6 +27,7 @@
import AppKit
import CommonLibrary
import CommonUtils
import PassepartoutKit
import ServiceManagement

View File

@ -1,5 +1,5 @@
//
// ProviderEntityViewProviding+Extensions.swift
// ProviderEntityViewProviding+VPN.swift
// Passepartout
//
// Created by Davide De Rosa on 10/29/24.

View File

@ -25,9 +25,10 @@
import PassepartoutKit
import SwiftUI
import UILibrary
extension DNSModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
DNSView(editor: editor, module: self)
}
}

View File

@ -25,9 +25,10 @@
import PassepartoutKit
import SwiftUI
import UILibrary
extension HTTPProxyModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
HTTPProxyView(editor: editor, module: self)
}
}

View File

@ -25,9 +25,10 @@
import PassepartoutKit
import SwiftUI
import UILibrary
extension IPModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
IPView(editor: editor, module: self)
}
}

View File

@ -25,9 +25,10 @@
import PassepartoutKit
import SwiftUI
import UILibrary
extension OnDemandModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
OnDemandView(editor: editor, module: self)
}
}

View File

@ -26,15 +26,16 @@
import CommonUtils
import PassepartoutKit
import SwiftUI
import UILibrary
extension OpenVPNModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
OpenVPNView(editor: editor, module: self, impl: impl as? OpenVPNModule.Implementation)
}
}
extension OpenVPNModule: ProviderEntityViewProviding {
func providerEntityView(
public func providerEntityView(
with provider: SerializedProvider,
errorHandler: ErrorHandler,
onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void

View File

@ -26,15 +26,16 @@
import CommonUtils
import PassepartoutKit
import SwiftUI
import UILibrary
extension WireGuardModule.Builder: ModuleViewProviding {
func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
public func moduleView(with editor: ProfileEditor, impl: ModuleImplementation?) -> some View {
WireGuardView(editor: editor, module: self, impl: impl as? WireGuardModule.Implementation)
}
}
extension WireGuardModule: ProviderEntityViewProviding {
func providerEntityView(
public func providerEntityView(
with provider: SerializedProvider,
errorHandler: ErrorHandler,
onSelect: @escaping (any ProviderEntity & Encodable) async throws -> Void

View File

@ -41,15 +41,6 @@ public enum AppFeature: String, CaseIterable {
case routing
case sharing
public static let fullV2Features: [AppFeature] = [
.dns,
.httpProxy,
.onDemand,
.providers,
.routing,
.sharing
]
}
extension AppFeature: Identifiable {

View File

@ -0,0 +1,30 @@
//
// AppFeatureProviding.swift
// Passepartout
//
// Created by Davide De Rosa on 10/11/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
protocol AppFeatureProviding {
var features: [AppFeature] { get }
}

View File

@ -0,0 +1,30 @@
//
// AppFeatureRequiring.swift
// Passepartout
//
// Created by Davide De Rosa on 11/17/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
public protocol AppFeatureRequiring {
var features: Set<AppFeature> { get }
}

View File

@ -23,9 +23,7 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import CommonUtils
import Foundation
import PassepartoutKit
public struct AppProduct: RawRepresentable, Hashable, Sendable {
public let rawValue: String

View File

@ -35,8 +35,10 @@ public enum AppUserLevel: Int, Sendable {
case fullV2 = 2
case subscriber = 3
}
var isFullVersion: Bool {
extension AppUserLevel {
public var isFullVersion: Bool {
switch self {
case .fullV2, .subscriber:
return true
@ -46,7 +48,7 @@ public enum AppUserLevel: Int, Sendable {
}
}
var isRestricted: Bool {
public var isRestricted: Bool {
switch self {
case .undefined, .beta:
return true

View File

@ -0,0 +1,37 @@
//
// AppFeature+Full.swift
// Passepartout
//
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
extension AppFeature {
public static let fullV2Features: [AppFeature] = [
.dns,
.httpProxy,
.onDemand,
.providers,
.routing,
.sharing
]
}

View File

@ -1,8 +1,8 @@
//
// Bundle+Extensions.swift
// AppFeatureProviding+Levels.swift
// Passepartout
//
// Created by Davide De Rosa on 8/27/24.
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
@ -25,16 +25,20 @@
import Foundation
extension Bundle {
public func unsafeDecode<T: Decodable>(_ type: T.Type, filename: String) -> T {
guard let jsonURL = url(forResource: filename, withExtension: "json") else {
fatalError("Unable to find \(filename).json in bundle")
}
do {
let data = try Data(contentsOf: jsonURL)
return try JSONDecoder().decode(type, from: data)
} catch {
fatalError("Unable to decode \(filename).json: \(error)")
extension AppUserLevel: AppFeatureProviding {
public var features: [AppFeature] {
switch self {
case .beta:
return [.interactiveLogin, .sharing]
case .fullV2:
return AppFeature.fullV2Features
case .subscriber:
return AppFeature.allCases
default:
return []
}
}
}

View File

@ -1,8 +1,8 @@
//
// AppFeatureProviding.swift
// AppFeatureProviding+Products.swift
// Passepartout
//
// Created by Davide De Rosa on 10/11/24.
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
@ -25,30 +25,8 @@
import Foundation
protocol AppFeatureProviding {
var features: [AppFeature] { get }
}
extension AppUserLevel: AppFeatureProviding {
var features: [AppFeature] {
switch self {
case .beta:
return [.interactiveLogin, .sharing]
case .fullV2:
return AppFeature.fullV2Features
case .subscriber:
return AppFeature.allCases
default:
return []
}
}
}
extension AppProduct: AppFeatureProviding {
var features: [AppFeature] {
public var features: [AppFeature] {
switch self {
// MARK: Current

View File

@ -1,5 +1,5 @@
//
// MockAppProductHelper.swift
// FakeAppProductHelper.swift
// Passepartout
//
// Created by Davide De Rosa on 12/19/23.
@ -27,12 +27,12 @@ import Combine
import CommonUtils
import Foundation
public actor MockAppProductHelper: AppProductHelper {
public actor FakeAppProductHelper: AppProductHelper {
private let build: Int
public private(set) var products: [AppProduct: InAppProduct]
public nonisolated let receiptReader: MockAppReceiptReader
public nonisolated let receiptReader: FakeAppReceiptReader
private nonisolated let didUpdateSubject: PassthroughSubject<Void, Never>
@ -40,7 +40,7 @@ public actor MockAppProductHelper: AppProductHelper {
public init(build: Int = .max) {
self.build = build
products = [:]
receiptReader = MockAppReceiptReader()
receiptReader = FakeAppReceiptReader()
didUpdateSubject = PassthroughSubject()
}

View File

@ -1,5 +1,5 @@
//
// MockAppReceiptReader.swift
// FakeAppReceiptReader.swift
// Passepartout
//
// Created by Davide De Rosa on 12/19/23.
@ -26,7 +26,7 @@
import CommonUtils
import Foundation
public actor MockAppReceiptReader: AppReceiptReader {
public actor FakeAppReceiptReader: AppReceiptReader {
private var localReceipt: InAppReceipt?
public init(receipt localReceipt: InAppReceipt? = nil) {

View File

@ -128,13 +128,6 @@ extension IAPManager {
features.allSatisfy(eligibleFeatures.contains)
}
public func isEligible(forProvider providerId: ProviderID) -> Bool {
if providerId == .oeck {
return true
}
return isEligible(for: .providers)
}
public func isEligibleForFeedback() -> Bool {
#if os(tvOS)
false

View File

@ -68,7 +68,6 @@ extension ProfileAttributes: CustomDebugStringConvertible {
// MARK: - ProfileUserInfoTransformable
// FIXME: #570, test user info encoding/decoding with JSONSerialization
extension ProfileAttributes: ProfileUserInfoTransformable {
public var userInfo: [String: AnyHashable]? {
do {

View File

@ -1,8 +1,8 @@
//
// AppFeatureRequiring.swift
// AppFeatureRequiring+Modules.swift
// Passepartout
//
// Created by Davide De Rosa on 11/17/24.
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
@ -26,38 +26,6 @@
import Foundation
import PassepartoutKit
public protocol AppFeatureRequiring {
var features: Set<AppFeature> { get }
}
// MARK: - Profile
extension Profile: AppFeatureRequiring {
public var features: Set<AppFeature> {
let builders = activeModules.compactMap { module in
guard let builder = module.moduleBuilder() else {
fatalError("Cannot produce ModuleBuilder from Module: \(module)")
}
return builder
}
return builders.features
}
}
extension Array: AppFeatureRequiring where Element == any ModuleBuilder {
public var features: Set<AppFeature> {
let requirements = compactMap { builder in
guard let requiring = builder as? AppFeatureRequiring else {
fatalError("ModuleBuilder does not implement AppFeatureRequiring: \(builder)")
}
return requiring
}
return Set(requirements.flatMap(\.features))
}
}
// MARK: - Modules
extension DNSModule.Builder: AppFeatureRequiring {
public var features: Set<AppFeature> {
[.dns]
@ -103,11 +71,3 @@ extension WireGuardModule.Builder: AppFeatureRequiring {
providerId?.features ?? []
}
}
// MARK: - Providers
extension ProviderID: AppFeatureRequiring {
public var features: Set<AppFeature> {
self != .oeck ? [.providers] : []
}
}

View File

@ -0,0 +1,51 @@
//
// AppFeatureRequiring+Profile.swift
// Passepartout
//
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutKit
extension Profile: AppFeatureRequiring {
public var features: Set<AppFeature> {
let builders = activeModules.compactMap { module in
guard let builder = module.moduleBuilder() else {
fatalError("Cannot produce ModuleBuilder from Module: \(module)")
}
return builder
}
return builders.features
}
}
extension Array: AppFeatureRequiring where Element == any ModuleBuilder {
public var features: Set<AppFeature> {
let requirements = compactMap { builder in
guard let requiring = builder as? AppFeatureRequiring else {
fatalError("ModuleBuilder does not implement AppFeatureRequiring: \(builder)")
}
return requiring
}
return Set(requirements.flatMap(\.features))
}
}

View File

@ -0,0 +1,33 @@
//
// AppFeatureRequiring+Providers.swift
// Passepartout
//
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation
import PassepartoutKit
extension ProviderID: AppFeatureRequiring {
public var features: Set<AppFeature> {
self != .oeck ? [.providers] : []
}
}

View File

@ -1,5 +1,5 @@
//
// PassepartoutConfiguration+Extensions.swift
// PassepartoutConfiguration+Logging.swift
// Passepartout
//
// Created by Davide De Rosa on 10/4/24.

View File

@ -23,6 +23,7 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
@_exported import CommonIAP
import Foundation
import PassepartoutKit

View File

@ -25,7 +25,6 @@
import CommonUtils
import Foundation
import Kvitto
import PassepartoutKit
public actor FallbackReceiptReader: AppReceiptReader {

View File

@ -31,4 +31,16 @@ extension Bundle {
.deletingLastPathComponent()
.appendingPathComponent("receipt") // could be "sandboxReceipt"
}
public func unsafeDecode<T: Decodable>(_ type: T.Type, filename: String) -> T {
guard let jsonURL = url(forResource: filename, withExtension: "json") else {
fatalError("Unable to find \(filename).json in bundle")
}
do {
let data = try Data(contentsOf: jsonURL)
return try JSONDecoder().decode(type, from: data)
} catch {
fatalError("Unable to decode \(filename).json: \(error)")
}
}
}

View File

@ -1,26 +1 @@
//
// Dummy.swift
// Passepartout
//
// Created by Davide De Rosa on 11/8/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import Foundation

View File

@ -29,7 +29,7 @@ import SwiftUI
extension ModuleBuilder where Self: ModuleViewProviding {
@MainActor
func preview(title: String = "") -> some View {
public func preview(title: String = "") -> some View {
NavigationStack {
moduleView(with: ProfileEditor(modules: [self]), impl: nil)
.navigationTitle(title)
@ -38,7 +38,7 @@ extension ModuleBuilder where Self: ModuleViewProviding {
}
@MainActor
func preview<C: View>(with content: (ProfileEditor, Self) -> C) -> some View {
public func preview<C: View>(with content: (ProfileEditor, Self) -> C) -> some View {
NavigationStack {
content(ProfileEditor(modules: [self]), self)
}

View File

@ -0,0 +1,34 @@
//
// ModuleDraftEditing+UI.swift
// Passepartout
//
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import SwiftUI
extension ModuleDraftEditing {
@MainActor
public var draft: Binding<Draft> {
editor[module]
}
}

View File

@ -1,5 +1,5 @@
//
// ProfileManager+Extensions.swift
// ProfileManager+Editing.swift
// Passepartout
//
// Created by Davide De Rosa on 9/3/24.
@ -29,7 +29,7 @@ import PassepartoutKit
@MainActor
extension ProfileManager {
func removeProfiles(at offsets: IndexSet) async {
public func removeProfiles(at offsets: IndexSet) async {
let idsToRemove = headers
.enumerated()
.filter {

View File

@ -0,0 +1,50 @@
//
// TunnelInstallationProviding+Extensions.swift
// Passepartout
//
// Created by Davide De Rosa on 11/20/24.
// Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
// https://github.com/passepartoutvpn
//
// This file is part of Passepartout.
//
// Passepartout is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Passepartout is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
import CommonLibrary
import Foundation
import PassepartoutKit
@MainActor
extension TunnelInstallationProviding {
public var installation: TunnelInstallation? {
guard let currentProfile = tunnel.currentProfile else {
return nil
}
guard let header = profileManager.headers.first(where: {
$0.id == currentProfile.id
}) else {
return nil
}
return TunnelInstallation(header: header, onDemand: currentProfile.onDemand)
}
public var currentProfile: Profile? {
guard let id = tunnel.currentProfile?.id else {
return nil
}
return profileManager.profile(withId: id)
}
}

View File

@ -34,8 +34,8 @@ extension AppContext {
public static func mock(withRegistry registry: Registry) -> AppContext {
let iapManager = IAPManager(
inAppHelper: MockAppProductHelper(),
receiptReader: MockAppReceiptReader(),
inAppHelper: FakeAppProductHelper(),
receiptReader: FakeAppReceiptReader(),
unrestrictedFeatures: [
.interactiveLogin,
.onDemand

View File

@ -26,18 +26,10 @@
import PassepartoutKit
import SwiftUI
protocol ModuleDraftEditing {
public protocol ModuleDraftEditing {
associatedtype Draft: ModuleBuilder
var editor: ProfileEditor { get }
var module: Draft { get }
}
extension ModuleDraftEditing {
@MainActor
var draft: Binding<Draft> {
editor[module]
}
}

View File

@ -26,7 +26,7 @@
import Foundation
import SwiftUI
protocol ModuleViewFactory: AnyObject {
public protocol ModuleViewFactory: AnyObject {
associatedtype Content: View
@MainActor

View File

@ -26,7 +26,7 @@
import PassepartoutKit
import SwiftUI
protocol ModuleViewProviding {
public protocol ModuleViewProviding {
associatedtype Content: View
@MainActor

View File

@ -27,7 +27,7 @@ import CommonUtils
import PassepartoutKit
import SwiftUI
protocol ProviderEntityViewProviding {
public protocol ProviderEntityViewProviding {
associatedtype EntityContent: View
@MainActor

View File

@ -25,32 +25,9 @@
import CommonLibrary
import Foundation
import PassepartoutKit
public protocol TunnelInstallationProviding {
var profileManager: ProfileManager { get }
var tunnel: ExtendedTunnel { get }
}
@MainActor
extension TunnelInstallationProviding {
public var installation: TunnelInstallation? {
guard let currentProfile = tunnel.currentProfile else {
return nil
}
guard let header = profileManager.headers.first(where: {
$0.id == currentProfile.id
}) else {
return nil
}
return TunnelInstallation(header: header, onDemand: currentProfile.onDemand)
}
public var currentProfile: Profile? {
guard let id = tunnel.currentProfile?.id else {
return nil
}
return profileManager.profile(withId: id)
}
}

View File

@ -23,13 +23,14 @@
// along with Passepartout. If not, see <http://www.gnu.org/licenses/>.
//
@testable import CommonIAP
@testable import CommonLibrary
import CommonUtils
import Foundation
import XCTest
final class IAPManagerTests: XCTestCase {
// private let inApp = MockAppProductHelper()
// private let inApp = FakeAppProductHelper()
private let olderBuildNumber = 500
@ -44,7 +45,7 @@ extension IAPManagerTests {
// MARK: Build products
func test_givenBuildProducts_whenOlder_thenFullVersion() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: olderBuildNumber, identifiers: [])
let sut = IAPManager(receiptReader: reader) { build in
if build <= self.defaultBuildNumber {
@ -57,7 +58,7 @@ extension IAPManagerTests {
}
func test_givenBuildProducts_whenNewer_thenFreeVersion() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: newerBuildNumber, products: [])
let sut = IAPManager(receiptReader: reader) { build in
if build <= self.defaultBuildNumber {
@ -72,7 +73,7 @@ extension IAPManagerTests {
// MARK: Eligibility
func test_givenPurchasedFeature_whenReloadReceipt_thenIsEligible() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(receiptReader: reader)
XCTAssertFalse(sut.isEligible(for: AppFeature.fullV2Features))
@ -85,7 +86,7 @@ extension IAPManagerTests {
}
func test_givenPurchasedFeatures_thenIsOnlyEligibleForFeatures() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: defaultBuildNumber, products: [
.Features.networkSettings
])
@ -101,7 +102,7 @@ extension IAPManagerTests {
}
func test_givenPurchasedAndCancelledFeature_thenIsNotEligible() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(
withBuild: defaultBuildNumber,
products: [.Full.allPlatforms],
@ -114,7 +115,7 @@ extension IAPManagerTests {
}
func test_givenFreeVersion_thenIsNotEligibleForAnyFeature() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = IAPManager(receiptReader: reader)
@ -126,7 +127,7 @@ extension IAPManagerTests {
}
func test_givenFreeVersion_thenIsNotEligibleForAppleTV() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: defaultBuildNumber, products: [])
let sut = IAPManager(receiptReader: reader)
@ -135,7 +136,7 @@ extension IAPManagerTests {
}
func test_givenFullV2Version_thenIsEligibleForAnyFeatureExceptExcluded() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Full.allPlatforms])
let sut = IAPManager(receiptReader: reader)
@ -155,7 +156,7 @@ extension IAPManagerTests {
}
func test_givenAppleTV_thenIsEligibleForAppleTV() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
await reader.setReceipt(withBuild: defaultBuildNumber, products: [.Features.appleTV])
let sut = IAPManager(receiptReader: reader)
@ -164,7 +165,7 @@ extension IAPManagerTests {
}
func test_givenPlatformVersion_thenIsFullVersionForPlatform() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(receiptReader: reader)
#if os(macOS)
@ -179,7 +180,7 @@ extension IAPManagerTests {
}
func test_givenPlatformVersion_thenIsNotFullVersionForOtherPlatform() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(receiptReader: reader)
#if os(macOS)
@ -196,7 +197,7 @@ extension IAPManagerTests {
// MARK: App level
func test_givenBetaApp_thenIsRestricted() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader)
await sut.reloadReceipt()
@ -204,7 +205,7 @@ extension IAPManagerTests {
}
func test_givenBetaApp_thenIsNotEligibleForAllFeatures() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader)
await sut.reloadReceipt()
@ -212,7 +213,7 @@ extension IAPManagerTests {
}
func test_givenBetaApp_thenIsEligibleForUserLevelFeatures() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader)
let eligible = AppUserLevel.beta.features
@ -222,7 +223,7 @@ extension IAPManagerTests {
}
func test_givenBetaApp_thenIsEligibleForUnrestrictedFeature() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .beta, receiptReader: reader, unrestrictedFeatures: [.onDemand])
var eligible = AppUserLevel.beta.features
@ -233,7 +234,7 @@ extension IAPManagerTests {
}
func test_givenFullV2App_thenIsFullVersion() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .fullV2, receiptReader: reader)
await sut.reloadReceipt()
@ -241,7 +242,7 @@ extension IAPManagerTests {
}
func test_givenSubscriberApp_thenIsFullVersion() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .subscriber, receiptReader: reader)
await sut.reloadReceipt()
@ -249,7 +250,7 @@ extension IAPManagerTests {
}
func test_givenFullV2App_thenIsEligibleForAnyFeatureExceptExcluded() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .fullV2, receiptReader: reader)
await sut.reloadReceipt()
@ -268,7 +269,7 @@ extension IAPManagerTests {
}
func test_givenSubscriberApp_thenIsEligibleForAnyFeature() async {
let reader = MockAppReceiptReader()
let reader = FakeAppReceiptReader()
let sut = IAPManager(customUserLevel: .subscriber, receiptReader: reader)
await sut.reloadReceipt()
@ -288,7 +289,7 @@ private extension IAPManager {
) {
self.init(
customUserLevel: customUserLevel,
inAppHelper: MockAppProductHelper(),
inAppHelper: FakeAppProductHelper(),
receiptReader: receiptReader,
unrestrictedFeatures: unrestrictedFeatures,
productsAtBuild: productsAtBuild