Files
Termind/FrameworkTests/SSHSettingsTests.swift
T

147 lines
6.1 KiB
Swift

import CloudKit
import Foundation
import SwiftData
import TermindApplication
import TermindDomain
import TermindPersistence
import Testing
@testable import TermindInfrastructure
@Suite @MainActor
struct SSHSettingsTests {
@Test
func defaultsAndRangeHaveOneSharedDefinition() {
#expect(SSHPreferences() == SSHPreferences(maxSessions: 10))
#expect(SettingsKey.defaultSSHMaxSessions == 10)
#expect(SettingsKey.sshMaxSessionsRange == 1...100)
#expect(SSHPreferences(maxSessions: 0).maxSessions == 1)
#expect(SSHPreferences(maxSessions: 101).maxSessions == 100)
}
@Test
func failedSavePreservesTheRuntimeValue() {
let persistence = TestSSHPreferencesPersistence()
let model = SSHSettingsModel(persistence: persistence)
var received: [SSHPreferences] = []
model.didChange = { received.append($0) }
persistence.fail = true
#expect(!model.set(20, for: \.maxSessions))
#expect(model.preferences == SSHPreferences())
#expect(received.isEmpty)
persistence.fail = false
#expect(model.set(20, for: \.maxSessions))
#expect(persistence.value == SSHPreferences(maxSessions: 20))
#expect(received == [SSHPreferences(maxSessions: 20)])
}
@Test
func cloudRoundTripUsesAnOldClientSafeRecordType() throws {
let source = try Fixture(maxSessions: 7)
let destination = try Fixture(maxSessions: 3)
defer {
source.cleanUp()
destination.cleanUp()
}
try source.store.saveSSH(SSHPreferences(maxSessions: 42))
let key = SettingsRecordKey(kind: .ssh, id: "default")
let outgoing = try #require(try source.store.journal.envelope(for: key))
let recordID = CKRecord.ID(
recordName: key.recordName,
zoneID: .init(zoneName: SettingsSyncCoordinator.zoneName))
let record = try SettingsSyncCoordinator.makeRecord(.init(envelope: outgoing), recordID: recordID)
#expect(record.recordType == SettingsSyncCoordinator.sshRecordType)
#expect(SettingsSyncCoordinator.acceptsRecordType(record.recordType))
let result = try destination.store.applyRemote([SettingsSyncCoordinator.envelope(from: record)])
#expect(result.snapshot.ssh == SSHPreferences(maxSessions: 42))
#expect(try destination.store.loadSSH() == SSHPreferences(maxSessions: 42))
#expect(destination.defaults.integer(forKey: SettingsKey.sshMaxSessions) == 42)
}
@Test
func invalidRemoteValueIsRejected() throws {
let fixture = try Fixture()
defer { fixture.cleanUp() }
let value = try JSONDecoder().decode(
SSHPreferences.self, from: Data(#"{"maxSessions":0}"#.utf8))
let key = SettingsRecordKey(kind: .ssh, id: "default")
let envelope = SettingsEnvelope(
key: key, version: .next(after: nil, writerID: UUID()), payload: .ssh(value))
#expect(!envelope.isValid)
#expect(throws: SettingsSyncPersistenceError.self) {
try fixture.store.applyRemote([envelope])
}
#expect(try fixture.store.loadSSH() == SSHPreferences())
}
@Test
func remoteSnapshotsWithoutSSHDoNotResetTheCurrentLimit() throws {
let fixture = try Fixture(maxSessions: 60)
defer { fixture.cleanUp() }
let payload = SettingsRecordPayload.webSearch(.init(isEnabled: false))
let result = try fixture.store.applyRemote([
SettingsEnvelope(
key: payload.key, version: .next(after: nil, writerID: UUID()), payload: payload
)
])
#expect(result.snapshot.ssh == SSHPreferences(maxSessions: 60))
#expect(try fixture.store.loadSSH() == SSHPreferences(maxSessions: 60))
}
@Test
func version22StoreMigratesAndSeedsSSHPreferences() throws {
let directory = FileManager.default.temporaryDirectory.appending(path: UUID().uuidString)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItem(at: directory) }
let url = directory.appending(path: "Store.sqlite")
let oldSchema = Schema(versionedSchema: TermindSchemaV22.self)
var old: ModelContainer? = try ModelContainer(
for: oldSchema,
configurations: ModelConfiguration(schema: oldSchema, url: url, cloudKitDatabase: .none))
old!.mainContext.insert(PersistentHostGroup(HostGroup(name: "Before SSH preferences")))
try old!.mainContext.save()
old = nil
let migrated = try TermindPersistence.makeContainer(url: url)
let domain = "SSHMigration.\(UUID())"
let defaults = try #require(UserDefaults(suiteName: domain))
defer { defaults.removePersistentDomain(forName: domain) }
defaults.set(17, forKey: SettingsKey.sshMaxSessions)
let store = SettingsSyncPersistence(context: migrated.mainContext, defaults: defaults)
try store.prepare()
#expect(try store.loadSSH() == SSHPreferences(maxSessions: 17))
#expect(try migrated.mainContext.fetchCount(FetchDescriptor<PersistentSSHPreferences>()) == 1)
}
private struct Fixture {
let domain = "SSHSettingsSync.\(UUID())"
let container: ModelContainer
let defaults: UserDefaults
let store: SettingsSyncPersistence
init(maxSessions: Int? = nil) throws {
container = try TermindPersistence.makeContainer(inMemory: true)
defaults = try #require(UserDefaults(suiteName: domain))
if let maxSessions { defaults.set(maxSessions, forKey: SettingsKey.sshMaxSessions) }
store = SettingsSyncPersistence(context: container.mainContext, defaults: defaults)
try store.prepare()
}
func cleanUp() { defaults.removePersistentDomain(forName: domain) }
}
}
@MainActor
private final class TestSSHPreferencesPersistence: SSHPreferencesPersisting {
var value = SSHPreferences()
var fail = false
func loadSSH() throws -> SSHPreferences { value }
func saveSSH(_ value: SSHPreferences) throws {
if fail { throw CocoaError(.fileWriteNoPermission) }
self.value = value
}
}