Files
Termind/TFITests/MobileMultiHostPickerTests.swift

146 lines
6.0 KiB
Swift

// SPDX-FileCopyrightText: 2026 Kaede Akino
// SPDX-License-Identifier: GPL-3.0-only
import Foundation
import TermindApplication
import TermindDomain
import Testing
import struct TermindDomain.Host
@testable import TermindIOS
@Suite @MainActor
struct MobileMultiHostPickerTests {
@Test
func metricsPickerStartsWithMonitoredHostsAndCanClearTheSelection() {
let hosts = [Host(name: "Alpha", host: "alpha.invalid"), Host(name: "Bravo", host: "bravo.invalid")]
let picker = MobileMultiHostPickerModel(
library: HostLibraryModel(hosts: hosts), useCase: .metrics, selected: [hosts[0].id])
picker.reconcileSelection()
#expect(picker.hosts.map(\.id) == hosts.map(\.id))
#expect(picker.orderedSelection == [hosts[0].id])
picker.toggle(hosts[0].id)
#expect(picker.orderedSelection.isEmpty)
}
@Test(arguments: [HostUseCase.metrics, .snippetExecution, .preferredTerminal, .sftp])
func filteredSelectAllPreservesOtherSelectionsAndLibraryOrder(useCase: HostUseCase) {
let hosts = ["Alpha", "Bravo", "Charlie"].enumerated().map { index, name in
Host(name: name, host: "\(name.lowercased()).invalid", sortIndex: Double(index))
}
let picker = MobileMultiHostPickerModel(
library: HostLibraryModel(hosts: hosts), useCase: useCase, selected: [])
picker.toggle(hosts[2].id)
picker.query = "Alpha"
picker.toggleAllResults()
#expect(picker.allResultsSelected)
#expect(picker.orderedSelection == [hosts[0].id, hosts[2].id])
picker.toggleAllResults()
#expect(picker.orderedSelection == [hosts[2].id])
#expect(!picker.allResultsSelected)
picker.query = "missing"
picker.toggleAllResults()
#expect(!picker.allResultsSelected)
#expect(picker.orderedSelection == [hosts[2].id])
picker.query = ""
picker.toggleAllResults()
#expect(picker.orderedSelection == hosts.map(\.id))
picker.toggleAllResults()
#expect(picker.orderedSelection.isEmpty)
}
@Test
func metricsExcludesMonitoredAndNonSSHHostsAndPrunesUnavailableSelections() {
let monitored = Host(name: "Monitored", host: "monitored.invalid")
var eligible = Host(name: "Available", host: "available.invalid")
let telnet = Host(name: "Telnet", host: "telnet.invalid", sshEnabled: false, telnetEnabled: true)
let library = HostLibraryModel(hosts: [monitored, eligible, telnet])
let picker = MobileMultiHostPickerModel(
library: library, useCase: .metrics, selected: [monitored.id, eligible.id, telnet.id, UUID()],
isEligible: { $0.id != monitored.id })
picker.reconcileSelection()
#expect(picker.hosts.map(\.id) == [eligible.id])
#expect(picker.selected == [eligible.id])
eligible.sshEnabled = false
eligible.telnetEnabled = true
library.upsert(eligible)
#expect(library.storageError == nil)
#expect(picker.orderedSelection.isEmpty)
picker.reconcileSelection()
#expect(picker.selected.isEmpty)
}
@Test
func batchMetricsAdditionHasNoIndependentLimitAndDeduplicates() {
let hosts = (0..<5).map { Host(name: "Host \($0)", host: "host\($0).invalid") }
let preferences = PickerMetricsPreferences()
let metrics = MobileMetricsModel(
preferences: preferences, hosts: HostLibraryModel(hosts: hosts), services: .testing)
defer { metrics.core.shutdown() }
metrics.pause()
let initial = metrics.add([hosts[0].id])
#expect(initial)
let batch = metrics.add(hosts[1...3].map(\.id))
#expect(batch)
#expect(metrics.selections.map(\.id) == Array(hosts.prefix(4)).map(\.id))
let repeated = metrics.add([hosts[0].id, hosts[1].id, hosts[1].id, hosts[2].id])
#expect(repeated)
#expect(metrics.selections.count == 4)
let final = metrics.add(hosts[3...4].map(\.id))
#expect(final)
#expect(metrics.selections.map(\.id) == hosts.map(\.id))
#expect(preferences.values.map(\.id) == hosts.map(\.id))
}
@Test
func batchMetricsSaveFailureDoesNotAddAnySelections() {
let hosts = (0..<2).map { Host(name: "Host \($0)", host: "host\($0).invalid") }
let preferences = PickerMetricsPreferences()
preferences.failsSaving = true
let metrics = MobileMetricsModel(
preferences: preferences, hosts: HostLibraryModel(hosts: hosts), services: .testing)
defer { metrics.core.shutdown() }
metrics.pause()
#expect(!metrics.add(hosts.map(\.id)))
#expect(metrics.selections.isEmpty)
#expect(preferences.values.isEmpty)
#expect(metrics.core.storageError != nil)
}
@Test
func mobileMetricsCanReplaceAndRemoveAllHosts() {
let hosts = (0..<4).map { Host(name: "Host \($0)", host: "host\($0).invalid") }
let preferences = PickerMetricsPreferences()
let metrics = MobileMetricsModel(
preferences: preferences, hosts: HostLibraryModel(hosts: hosts), services: .testing)
defer { metrics.core.shutdown() }
metrics.pause()
#expect(metrics.add(Array(hosts.prefix(3)).map(\.id)))
let editable = Set(hosts.map(\.id))
#expect(
metrics.replaceHosts(
selectedIDs: [hosts[0].id, hosts[2].id, hosts[3].id], editableHostIDs: editable))
#expect(metrics.selections.map(\.id) == [hosts[0].id, hosts[2].id, hosts[3].id])
#expect(metrics.replaceHosts(selectedIDs: [], editableHostIDs: editable))
#expect(metrics.selections.isEmpty)
#expect(preferences.values.isEmpty)
}
}
private final class PickerMetricsPreferences: MetricsConfigurationStoring {
var values: [MetricsSelection] = []
var failsSaving = false
func load() -> [MetricsSelection] { values }
func save(_ selections: [MetricsSelection]) throws {
if failsSaving { throw CocoaError(.fileWriteUnknown) }
values = selections
}
}