Files
Termind/TFITests/MobileWorkspaceStoreTests.swift
T

118 lines
5.2 KiB
Swift

import Foundation
import Testing
@testable import TermindIOS
@Suite @MainActor
struct MobileWorkspaceStoreTests {
@Test func emptyLegacyArrayMigratesAndCanBeSavedAgain() throws {
try withStore("[]") { store, defaults in
#expect(try store.load().isEmpty)
let data = try #require(defaults.data(forKey: MobileWorkspaceStore.key))
let object = try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
#expect(object["version"] as? Int == 1)
#expect((object["workspaces"] as? [Any])?.isEmpty == true)
#expect(try store.load().isEmpty)
let descriptor = MobileWorkspaceDescriptor(hostID: UUID(), title: "New", kind: .ssh)
try store.save([descriptor])
#expect(try MobileWorkspaceStore(defaults: defaults).load() == [descriptor])
}
}
@Test func legacyRecordsKeepIDsTitlesAndKindsWhileDiscardingIndices() throws {
let host = UUID()
let other = UUID()
let ids = (0..<5).map { _ in UUID() }
let json = """
[
{"id":"\(ids[0])","hostID":"\(host)","title":"Custom","kind":"ssh"},
{"id":"\(ids[1])","hostID":"\(host)","title":"Explicit","kind":"ssh","duplicateIndex":0},
{"id":"\(ids[2])","hostID":"\(host)","title":"Third","kind":"ssh"},
{"id":"\(ids[3])","hostID":"\(host)","title":"Files","kind":"sftp"},
{"id":"\(ids[4])","hostID":"\(other)","title":"Other","kind":"ssh"}
]
"""
try withStore(json) { store, defaults in
let descriptors = try store.load()
#expect(descriptors.map(\.id) == ids)
#expect(descriptors.map(\.title) == ["Custom", "Explicit", "Third", "Files", "Other"])
#expect(descriptors.map(\.kind) == [.ssh, .ssh, .ssh, .sftp, .ssh])
let migrated = defaults.data(forKey: MobileWorkspaceStore.key)
#expect(!String(decoding: try #require(migrated), as: UTF8.self).contains("duplicateIndex"))
#expect(try MobileWorkspaceStore(defaults: defaults).load() == descriptors)
#expect(defaults.data(forKey: MobileWorkspaceStore.key) == migrated)
defaults.set(Data(json.utf8), forKey: MobileWorkspaceStore.key)
#expect(try store.load() == descriptors)
}
}
@Test func legacyRecordsWithoutIndicesPreserveTheirTitles() throws {
let host = UUID()
let json = """
[
{"id":"\(UUID())","hostID":"\(host)","title":"One","kind":"ssh"},
{"id":"\(UUID())","hostID":"\(host)","title":"Two","kind":"ssh"},
{"id":"\(UUID())","hostID":"\(host)","title":"Files","kind":"sftp"}
]
"""
try withStore(json) { store, _ in
let descriptors = try store.load()
#expect(descriptors.map(\.title) == ["One", "Two", "Files"])
}
}
@Test func currentSnapshotRoundTripsWithoutMigration() throws {
try withStore("[]") { store, defaults in
let descriptors = [
MobileWorkspaceDescriptor(hostID: UUID(), title: "Custom", kind: .sftp)
]
try store.save(descriptors)
let data = defaults.data(forKey: MobileWorkspaceStore.key)
#expect(try store.load() == descriptors)
#expect(defaults.data(forKey: MobileWorkspaceStore.key) == data)
}
}
@Test(arguments: [
"broken", "null", "{}", "[{}]", #"{"version":99,"workspaces":[]}"#,
#"{"version":1,"workspaces":[{}]}"#,
])
func invalidDataIsPreservedUntilASuccessfulLoad(json: String) throws {
try withStore(json) { store, defaults in
#expect(throws: (any Error).self) { try store.load() }
#expect(throws: (any Error).self) { try store.save([]) }
#expect(defaults.data(forKey: MobileWorkspaceStore.key) == Data(json.utf8))
defaults.set(Data("[]".utf8), forKey: MobileWorkspaceStore.key)
#expect(try store.load().isEmpty)
try store.save([])
}
}
@Test
func duplicateLegacyIDsAreNotMigrated() throws {
let id = UUID()
let host = UUID()
let json = """
[
{"id":"\(id)","hostID":"\(host)","title":"One","kind":"ssh","duplicateIndex":0},
{"id":"\(id)","hostID":"\(host)","title":"Two","kind":"ssh","duplicateIndex":1}
]
"""
try withStore(json) { store, defaults in
#expect(throws: (any Error).self) { try store.load() }
#expect(throws: (any Error).self) { try store.save([]) }
#expect(defaults.data(forKey: MobileWorkspaceStore.key) == Data(json.utf8))
}
}
private func withStore(
_ json: String, check: (MobileWorkspaceStore, UserDefaults) throws -> Void
) throws {
let suite = "termind.workspace-store-tests.\(UUID())"
let defaults = try #require(UserDefaults(suiteName: suite))
defer { defaults.removePersistentDomain(forName: suite) }
defaults.set(Data(json.utf8), forKey: MobileWorkspaceStore.key)
try check(MobileWorkspaceStore(defaults: defaults), defaults)
}
}