passepartout-apple/Library/Tests/CommonLibraryTests/Business/ExtendedTunnelTests.swift
Davide aeec943c58
Move ModulePreferences to Profile.userInfo (#993)
Store module preferences in the Profile.userInfo field for atomicity.
Access and modification are dramatically simplified, and synchronization
comes for free.

On the other side, fix provider preferences synchronization by using
viewContext for the CloudKit container.

Fixes #992
2024-12-10 11:18:52 +01:00

172 lines
6.2 KiB
Swift

//
// ExtendedTunnelTests.swift
// Passepartout
//
// Created by Davide De Rosa on 9/12/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 Combine
@testable import CommonLibrary
import Foundation
import PassepartoutKit
import XCTest
final class ExtendedTunnelTests: XCTestCase {
private var subscriptions: Set<AnyCancellable> = []
}
@MainActor
extension ExtendedTunnelTests {
func test_givenTunnel_whenDisconnectWithError_thenPublishesLastErrorCode() async throws {
let env = InMemoryEnvironment()
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, interval: 0.1)
let module = try DNSModule.Builder().tryBuild()
let profile = try Profile.Builder(modules: [module], activatingModules: true).tryBuild()
try await sut.connect(with: profile)
env.setEnvironmentValue(.crypto, forKey: TunnelEnvironmentKeys.lastErrorCode)
let exp = expectation(description: "Last error code")
var didCall = false
sut
.$lastErrorCode
.sink {
if !didCall, $0 != nil {
didCall = true
exp.fulfill()
}
}
.store(in: &subscriptions)
try await tunnel.disconnect()
await fulfillment(of: [exp], timeout: CommonLibraryTests.timeout)
XCTAssertEqual(sut.lastErrorCode, .crypto)
}
func test_givenTunnel_whenConnect_thenPublishesDataCount() async throws {
let env = InMemoryEnvironment()
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, interval: 0.1)
let module = try DNSModule.Builder().tryBuild()
let profile = try Profile.Builder(modules: [module], activatingModules: true).tryBuild()
try await sut.install(profile)
let dataCount = DataCount(500, 700)
env.setEnvironmentValue(dataCount, forKey: TunnelEnvironmentKeys.dataCount)
XCTAssertEqual(sut.dataCount, nil)
let exp = expectation(description: "Data count")
var didCall = false
sut
.$dataCount
.sink {
if !didCall, $0 != nil {
didCall = true
exp.fulfill()
}
}
.store(in: &subscriptions)
try await tunnel.install(profile, connect: true, title: \.name)
await fulfillment(of: [exp], timeout: CommonLibraryTests.timeout)
XCTAssertEqual(sut.dataCount, dataCount)
}
func test_givenTunnelAndProcessor_whenInstall_thenProcessesProfile() async throws {
let env = InMemoryEnvironment()
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
let processor = MockTunnelProcessor()
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, processor: processor, interval: 0.1)
let module = try DNSModule.Builder().tryBuild()
let profile = try Profile.Builder(modules: [module], activatingModules: true).tryBuild()
try await sut.install(profile)
XCTAssertEqual(tunnel.currentProfile?.id, profile.id)
// XCTAssertEqual(processor.titleCount, 1) // unused by FakeTunnelStrategy
XCTAssertEqual(processor.willInstallCount, 1)
}
func test_givenTunnel_whenStatusChanges_thenConnectionStatusIsExpected() async throws {
let env = InMemoryEnvironment()
let tunnel = Tunnel(strategy: FakeTunnelStrategy(environment: env))
let processor = MockTunnelProcessor()
let sut = ExtendedTunnel(tunnel: tunnel, environment: env, processor: processor, interval: 0.1)
let module = try DNSModule.Builder().tryBuild()
let profile = try Profile.Builder(modules: [module], activatingModules: true).tryBuild()
try await sut.install(profile)
XCTAssertEqual(tunnel.currentProfile?.id, profile.id)
// XCTAssertEqual(processor.titleCount, 1) // unused by FakeTunnelStrategy
XCTAssertEqual(processor.willInstallCount, 1)
}
func test_givenTunnelStatus_thenConnectionStatusIsExpected() async throws {
let allTunnelStatuses: [TunnelStatus] = [
.inactive,
.activating,
.active,
.deactivating
]
let allConnectionStatuses: [ConnectionStatus] = [
.disconnected,
.connecting,
.connected,
.disconnecting
]
let env = InMemoryEnvironment()
let key = TunnelEnvironmentKeys.connectionStatus
// no connection status, tunnel status unaffected
allTunnelStatuses.forEach {
XCTAssertEqual($0.withEnvironment(env), $0)
}
// has connection status
// affected if .active
let tunnelActive: TunnelStatus = .active
env.setEnvironmentValue(ConnectionStatus.connected, forKey: key)
XCTAssertEqual(tunnelActive.withEnvironment(env), .active)
allConnectionStatuses
.filter {
$0 != .connected
}
.forEach {
env.setEnvironmentValue($0, forKey: key)
XCTAssertEqual(tunnelActive.withEnvironment(env), .activating)
}
// unaffected otherwise
allTunnelStatuses
.filter {
$0 != .active
}
.forEach {
XCTAssertEqual($0.withEnvironment(env), $0)
}
}
}