Files
Termind/FrameworkTests/ForwardingLibraryTests.swift
T

98 lines
4.0 KiB
Swift

import Foundation
import TermindApplication
import TermindDomain
import Testing
import struct TermindDomain.Host
@Suite @MainActor
struct ForwardingLibraryTests {
@Test func rulesSaveRebindAndDeleteWithoutRewritingHosts() throws {
let first = Host(name: "First", host: "first.invalid")
let second = Host(name: "Second", host: "second.invalid")
let store = HostLibraryModel(hosts: [first, second])
let model = ForwardingLibraryModel(library: store)
var rule = PortForwardingRule(hostID: first.id, label: "Database")
try model.save(rule)
let savedHosts = store.hosts
rule.hostID = second.id
try model.save(rule)
#expect(model.rules == [rule])
#expect(store.hosts == savedHosts)
#expect(store.portForwardings(for: first.id).isEmpty)
#expect(model.search("second").map(\.id) == [rule.id])
#expect(model.search("Database").map(\.id) == [rule.id])
#expect(model.search("8080").map(\.id) == [rule.id])
try model.delete(rule.id)
#expect(model.rules.isEmpty)
#expect(store.hosts == savedHosts)
}
@Test func failedSavesAndDeletesRollbackTheRuleSnapshot() throws {
let host = Host(name: "Server", host: "server.invalid")
let other = Host(name: "Other", host: "other.invalid")
let rule = PortForwardingRule(hostID: host.id)
let persistence = ForwardingPersistence(snapshot: VaultSnapshot(hosts: [host, other], portForwardings: [rule]))
let store = HostLibraryModel(persistence: persistence)
let model = ForwardingLibraryModel(library: store)
persistence.fails = true
var changed = rule
changed.hostID = other.id
changed.bindPort = 9000
changed.colorTag = .orange
#expect(throws: (any Error).self) { try model.save(changed) }
#expect(model.rules == [rule])
#expect(persistence.snapshot.portForwardings == [rule])
#expect(store.hosts == [host, other])
#expect(throws: (any Error).self) { try model.delete(rule.id) }
#expect(model.rules == [rule])
let new = PortForwardingRule(hostID: host.id, bindPort: 9090)
#expect(throws: (any Error).self) { try model.save(new) }
#expect(model.rules == [rule])
persistence.fails = false
let saved = try model.save(changed)
#expect(saved.label == "Other (-L)")
changed.label = saved.label
#expect(model.rules == [changed])
}
@Test func invalidHostIsRejectedAndOrphansRemainDeletable() throws {
let orphan = PortForwardingRule(hostID: UUID())
let store = HostLibraryModel(portForwardings: [orphan])
let model = ForwardingLibraryModel(library: store)
#expect(throws: (any Error).self) { try model.save(orphan) }
try model.delete(orphan.id)
#expect(model.rules.isEmpty)
}
@Test func hostSaveFailurePreservesItsConfigurationAndRules() {
var host = Host(name: "Server", host: "server.invalid")
let rule = PortForwardingRule(hostID: host.id)
let persistence = ForwardingPersistence(snapshot: VaultSnapshot(hosts: [host], portForwardings: [rule]))
let store = HostLibraryModel(persistence: persistence)
persistence.fails = true
host.port = 2222
#expect(!store.saveHost(host))
#expect(store.host(host.id)?.port == 22)
#expect(store.portForwardings == [rule])
}
}
@MainActor
private final class ForwardingPersistence: VaultPersisting {
var snapshot: VaultSnapshot
var fails = false
init(snapshot: VaultSnapshot) { self.snapshot = snapshot }
func load() throws -> VaultSnapshot { snapshot }
func save(
_ snapshot: VaultSnapshot, touching keys: Set<VaultRecordKey>
) throws -> VaultMutationSet {
if fails { throw CocoaError(.fileWriteUnknown) }
self.snapshot = snapshot
return .empty
}
func applyRemote(_ envelopes: [VaultEnvelope]) throws -> VaultRemoteApplyResult {
VaultRemoteApplyResult(snapshot: snapshot, acceptedKeys: [], needsUpload: [])
}
}