diff --git a/FrameworkTests/Fixtures/README.md b/FrameworkTests/Fixtures/README.md deleted file mode 100644 index 14e44801..00000000 --- a/FrameworkTests/Fixtures/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# Original-module SwiftData stores - -V1.store and V14.store were generated from the original `Termind` app module -before extracting `TermindPersistence`. They contain only synthetic data: a -fixed-ID host group and, in V1, a legacy provider. SQLite's backup API copied -committed contents from the WAL into these self-contained fixtures. - -Do not regenerate these fixtures using the extracted models: that would stop -checking compatibility across the original and framework module identities. -The source revision is recorded below. -726a2eb909adba3ced6a507c4704886f787e5f66 diff --git a/FrameworkTests/Fixtures/V1.store b/FrameworkTests/Fixtures/V1.store deleted file mode 100644 index ad9654d7..00000000 Binary files a/FrameworkTests/Fixtures/V1.store and /dev/null differ diff --git a/FrameworkTests/Fixtures/V14.store b/FrameworkTests/Fixtures/V14.store deleted file mode 100644 index 27484777..00000000 Binary files a/FrameworkTests/Fixtures/V14.store and /dev/null differ diff --git a/FrameworkTests/Fixtures/V28.store b/FrameworkTests/Fixtures/V28.store deleted file mode 100644 index cdee5966..00000000 Binary files a/FrameworkTests/Fixtures/V28.store and /dev/null differ diff --git a/FrameworkTests/PersistenceCompatibilityTests.swift b/FrameworkTests/PersistenceCompatibilityTests.swift index ef5fd2a8..bb862177 100644 --- a/FrameworkTests/PersistenceCompatibilityTests.swift +++ b/FrameworkTests/PersistenceCompatibilityTests.swift @@ -4,8 +4,6 @@ import TermindDomain import TermindPersistence import Testing -private final class FixtureBundle: NSObject {} - @Suite @MainActor struct PersistenceCompatibilityTests { @Test func version35DropsRetiredImporterTableAndPreservesNativeHistoryOnReopen() throws { @@ -111,27 +109,6 @@ struct PersistenceCompatibilityTests { #expect(SSHKeyRef(label: "").storage == .iCloud) } - @Test(arguments: [1, 14]) - func originalAppModuleStoresOpenWithoutLosingData(version: Int) throws { - let source = try #require( - Bundle(for: FixtureBundle.self).url(forResource: "V\(version)", withExtension: "store")) - let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) - defer { try? FileManager.default.removeItem(at: directory) } - let url = directory.appendingPathComponent("Termind.store") - try FileManager.default.copyItem(at: source, to: url) - let container = try TermindPersistence.makeContainer(url: url) - let groups = try container.mainContext.fetch(FetchDescriptor()) - #expect(groups.map(\.name) == ["Legacy fixture group"]) - #expect(groups.first?.id == UUID(uuidString: "11111111-1111-1111-1111-111111111111")) - if version == 1 { - let providers = try container.mainContext.fetch(FetchDescriptor()) - #expect(providers.map(\.name) == ["Legacy fixture provider"]) - } - #expect(try container.mainContext.fetchCount(FetchDescriptor()) == 0) - #expect(try container.mainContext.fetchCount(FetchDescriptor()) == 0) - } - @Test func fileStoreReopensWithSharedModels() throws { let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) @@ -149,93 +126,6 @@ struct PersistenceCompatibilityTests { try container.mainContext.save() } - @Test - func version28FixtureMigratesAndReopensWithoutLosingData() throws { - let source = try #require(Bundle(for: FixtureBundle.self).url(forResource: "V28", withExtension: "store")) - let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) - defer { try? FileManager.default.removeItem(at: directory) } - let url = directory.appendingPathComponent("Termind.store") - try FileManager.default.copyItem(at: source, to: url) - - try verifyVersion28Store(at: url) - try verifyVersion28Store(at: url) - } - - private func verifyVersion28Store(at url: URL) throws { - let container = try TermindPersistence.makeContainer(url: url) - let context = container.mainContext - let snapshot = try HostPersistence(context: context).load() - let host = try #require(snapshot.hosts.first) - let identity = try #require(snapshot.identities.first) - let key = try #require(snapshot.keys.first) - let snippet = try #require(snapshot.snippets.first) - let installationID = UUID(uuidString: "66666666-6666-6666-6666-666666666666")! - - #expect(snapshot.hosts.count == 1) - #expect(host.id == UUID(uuidString: "22222222-2222-2222-2222-222222222222")) - #expect(host.name == "V28 fixture host") - #expect(host.host == "legacy.example.invalid") - #expect(host.sshEnabled) - #expect(host.sshPort == 2222) - #expect(host.sshCharset == .gb18030) - #expect(!host.telnetEnabled) - #expect(host.telnetPort == 23) - #expect(host.telnetCharset == .utf8) - #expect(host.telnetIdentityID == nil) - #expect(host.sshIdentityID == identity.id) - #expect(host.groupID == snapshot.groups.first?.id) - #expect(snapshot.groups.map(\.name) == ["V28 fixture group"]) - #expect(host.tags == ["legacy", "ssh"]) - #expect(host.colorTag == .teal) - #expect(host.sortIndex == 2.5) - #expect(host.shellIntegrationMode == .disabled) - #expect(host.portForwardingMaxConnections == 3) - #expect(host.sftpChannelInitialPath == "/srv/legacy") - #expect( - host.environmentVariables == [ - HostEnvironmentVariable(variable: "LANG", value: "zh_CN.GB18030"), - HostEnvironmentVariable(variable: "TERM", value: "xterm-256color"), - ]) - #expect(host.startupSnippetID == snippet.id) - #expect(snippet.body == "pwd") - #expect(snippet.targetHostIDs == [host.id]) - #expect(identity.id == UUID(uuidString: "33333333-3333-3333-3333-333333333333")) - #expect(identity.username == "deploy") - #expect(identity.credentialRef == "termind.fixture.password") - #expect(identity.passwordStorage == .thisDevice) - #expect(identity.keyID == key.id) - #expect(identity.notes == "Synthetic fixture") - #expect(identity.colorTag == .purple) - #expect(key.id == UUID(uuidString: "44444444-4444-4444-4444-444444444444")) - #expect(key.label == "V28 fixture key") - #expect( - snapshot.knownHosts == [ - KnownHostEntry( - id: UUID(uuidString: "77777777-7777-7777-7777-777777777777")!, - hostname: "legacy.example.invalid", port: 2222, - fingerprint: "SHA256:fixture", addedAt: Date(timeIntervalSince1970: 50)) - ]) - try HistorySyncPersistence(context: context).prepare() - let history = try context.fetch(FetchDescriptor()).map(\.value) - #expect( - history == [ - ConnectionHistoryEntry( - id: UUID(uuidString: "55555555-5555-5555-5555-555555555555")!, - hostID: host.id, kind: .ssh, connectedAt: Date(timeIntervalSince1970: 90), - sourceDeviceID: installationID) - ]) - #expect(try context.fetch(FetchDescriptor()).first?.installationID == installationID) - #expect(try context.fetch(FetchDescriptor()).map(\.id) == [installationID]) - - let suite = "PersistenceV28.\(UUID())" - let defaults = try #require(UserDefaults(suiteName: suite)) - defer { defaults.removePersistentDomain(forName: suite) } - let settings = SettingsSyncPersistence(context: context, defaults: defaults) - try settings.prepare() - #expect(try settings.loadWorkspacePreferences() == WorkspacePreferences()) - } - @Test func memoryContainersUseTheFullSchemaAndRemainIndependent() throws { let first = try TermindPersistence.makeContainer(inMemory: true) diff --git a/Scripts/generate-v28-fixture.swift b/Scripts/generate-v28-fixture.swift deleted file mode 100644 index 172a7a39..00000000 --- a/Scripts/generate-v28-fixture.swift +++ /dev/null @@ -1,71 +0,0 @@ -import Foundation -import SwiftData -import TermindDomain -import TermindPersistence - -// V28.store source: 0dc173d4b1d190071d35bc66f80ebb767d8b950c (the parent of b0d35d24). -// Build that checkout's TermindPersistence scheme for macOS with -onlyUsePackageVersionsFromResolvedFile. -// Compile this generator with -parse-as-library, using that build's -F/-I products, GRDBSQLite module path, -// TermindPersistence/TermindDomain/TermindApplication frameworks, and AgentKit.o/Logging.o. -// Pass a new temporary store path, then use SQLite's backup API to produce the standalone fixture after exit. -// Never regenerate against current models. All data is synthetic; no password or private key is stored. -// Expected schema checksum: hh/HY7td7OFShC/UhxiuwAlJuf8IZOWsZm7U7VqLaSs= -@main -struct GenerateV28Fixture { - @MainActor - static func main() throws { - let url = URL(fileURLWithPath: CommandLine.arguments[1]) - let schema = Schema(versionedSchema: TermindSchemaV28.self) - let container = try ModelContainer( - for: schema, - configurations: [ModelConfiguration("Termind", schema: schema, url: url, cloudKitDatabase: .none)]) - let context = container.mainContext - let groupID = UUID(uuidString: "11111111-1111-1111-1111-111111111111")! - let hostID = UUID(uuidString: "22222222-2222-2222-2222-222222222222")! - let identityID = UUID(uuidString: "33333333-3333-3333-3333-333333333333")! - let keyID = UUID(uuidString: "44444444-4444-4444-4444-444444444444")! - let historyID = UUID(uuidString: "55555555-5555-5555-5555-555555555555")! - let installationID = UUID(uuidString: "66666666-6666-6666-6666-666666666666")! - let knownHostID = UUID(uuidString: "77777777-7777-7777-7777-777777777777")! - let snippetID = UUID(uuidString: "88888888-8888-8888-8888-888888888888")! - let host = TermindSchemaV28HostModels.PersistentHost( - Host( - id: hostID, name: "V28 fixture host", host: "legacy.example.invalid", port: 2222, - identityID: identityID, charset: .gb18030, groupID: groupID, - tags: ["legacy", "ssh"], colorTag: .teal, sortIndex: 2.5, - shellIntegrationMode: .disabled, startupSnippetID: snippetID, - environmentVariables: [ - HostEnvironmentVariable(variable: "LANG", value: "zh_CN.GB18030"), - HostEnvironmentVariable(variable: "TERM", value: "xterm-256color"), - ], portForwardingMaxConnections: 3)) - host.sftpSessionInitialPath = "/srv/legacy" - host.lastUsedAt = Date(timeIntervalSince1970: 100) - context.insert(host) - context.insert(PersistentHostGroup(HostGroup(id: groupID, name: "V28 fixture group"))) - context.insert( - PersistentIdentity( - Identity( - id: identityID, name: "V28 fixture identity", username: "deploy", - credentialRef: "termind.fixture.password", passwordStorage: .thisDevice, - keyID: keyID, notes: "Synthetic fixture", colorTag: .purple))) - context.insert(PersistentSSHKey(SSHKeyRef(id: keyID, label: "V28 fixture key"))) - let knownHost = TermindSchemaV29KnownHostModels.PersistentKnownHost( - KnownHostEntry( - id: knownHostID, hostname: "legacy.example.invalid", port: 2222, - fingerprint: "SHA256:fixture", addedAt: Date(timeIntervalSince1970: 50))) - knownHost.isMismatched = true - context.insert(knownHost) - context.insert( - PersistentSnippet( - Snippet(id: snippetID, title: "V28 fixture snippet", body: "pwd", targetHostIDs: [hostID]))) - context.insert( - PersistentConnectionHistoryEntry( - ConnectionHistoryEntry( - id: historyID, hostID: hostID, kind: .ssh, connectedAt: Date(timeIntervalSince1970: 90)))) - let state = PersistentVaultSyncState() - state.installationID = installationID - state.bootstrapCompleted = true - context.insert(state) - try context.save() - } -} diff --git a/TFITests/MobileAppTests.swift b/TFITests/MobileAppTests.swift index 36a7f77a..6fedc367 100644 --- a/TFITests/MobileAppTests.swift +++ b/TFITests/MobileAppTests.swift @@ -13,8 +13,6 @@ import UIKit @testable import TermindIOS -private final class MobileMigrationFixtureBundle: NSObject {} - @Suite @MainActor struct MobileAppTests { private final class MetricsPreferences: MetricsConfigurationStoring { @@ -52,37 +50,6 @@ struct MobileAppTests { await app.tailscale.shutdown() } - @Test func startupLoadsVersion28DataAfterMigrationAndRelaunch() throws { - let source = try #require( - Bundle(for: MobileMigrationFixtureBundle.self).url(forResource: "V28", withExtension: "store")) - let directory = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) - try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true) - defer { try? FileManager.default.removeItem(at: directory) } - let url = directory.appendingPathComponent("Termind.store") - try FileManager.default.copyItem(at: source, to: url) - let suite = "MobileMigrationV28.\(UUID())" - let defaults = try #require(UserDefaults(suiteName: suite)) - defer { defaults.removePersistentDomain(forName: suite) } - - for _ in 0..<2 { - var dependencies = MobileAppDependencies.testing(defaults: defaults) - dependencies.container = try TermindPersistence.makeContainer(url: url) - let app = try MobileAppModel(dependencies: dependencies) - defer { app.outbox.stop() } - let host = try #require(app.library.hosts.first) - #expect(host.name == "V28 fixture host") - #expect(host.sshCharset == .gb18030) - #expect(host.sshPort == 2222) - #expect(host.sshIdentityID == app.library.identities.first?.id) - #expect(app.library.identities.first?.credentialRef == "termind.fixture.password") - let history = app.connectionHistory.entries(for: host.id) - #expect(history.count == 1) - #expect(history.first?.sourceDeviceID == UUID(uuidString: "66666666-6666-6666-6666-666666666666")) - #expect(history.first?.endedAt == nil) - #expect(try app.hosts.snapshot().value.hosts == [host]) - } - } - @Test func scenesCanUseTheSameAppRepository() throws { let repository = InMemoryHostRepository() let app = try MobileAppModel(hosts: repository) diff --git a/Termind.xcodeproj/project.pbxproj b/Termind.xcodeproj/project.pbxproj index 3d19aab4..b1195f69 100644 --- a/Termind.xcodeproj/project.pbxproj +++ b/Termind.xcodeproj/project.pbxproj @@ -85,7 +85,6 @@ D7D1BBB65020F61F7EFDDFD7 /* GhosttyTerminal in Frameworks */ = {isa = PBXBuildFile; productRef = TRMD00000000000000100012 /* GhosttyTerminal */; }; DEVK00000000000000000003 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = DEVK00000000000000000002 /* DeviceKit */; }; DEVK00000000000000000004 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = DEVK00000000000000000002 /* DeviceKit */; }; - E28A00000000000000000002 /* V28.store in Resources */ = {isa = PBXBuildFile; fileRef = E28A00000000000000000001 /* V28.store */; }; E635C54CA9F1A63D909CFCE6 /* TermindPersistence.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 244632CCDB1CA43F45C0EB54 /* TermindPersistence.framework */; }; E9A6C576AF3870A1958F9C5A /* ETSession in Frameworks */ = {isa = PBXBuildFile; productRef = 6335BE33C2C5F92BE0568DF6 /* ETSession */; }; EF2F88E4809C845C3F1BBF63 /* TermindDomain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A62058825D6607920F1E2A1 /* TermindDomain.framework */; }; @@ -492,7 +491,6 @@ BENC00000000000000000011 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; BENC00000000000000000012 /* termind-port-forwarding-benchmark */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "termind-port-forwarding-benchmark"; sourceTree = BUILT_PRODUCTS_DIR; }; DBE378B130478CFB00B16AA1 /* Termind.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; name = Termind.storekit; path = StoreKit/Termind.storekit; sourceTree = ""; }; - E28A00000000000000000001 /* V28.store */ = {isa = PBXFileReference; lastKnownFileType = file; name = V28.store; path = FrameworkTests/Fixtures/V28.store; sourceTree = SOURCE_ROOT; }; F04700000000000000000011 /* TFMTests/SSHForwardingPumpTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFMTests/SSHForwardingPumpTests.swift; sourceTree = SOURCE_ROOT; }; F04700000000000000000012 /* TFMTests/SSHForwardingWireTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TFMTests/SSHForwardingWireTests.swift; sourceTree = SOURCE_ROOT; }; F1BA00000000000000000020 /* Configuration/Firebase/GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Configuration/Firebase/GoogleService-Info.plist"; sourceTree = SOURCE_ROOT; }; @@ -847,14 +845,6 @@ path = Frameworks; sourceTree = ""; }; - DB9666953064BA9A005FB585 /* Persistence Test Fixtures */ = { - isa = PBXGroup; - children = ( - E28A00000000000000000001 /* V28.store */, - ); - name = "Persistence Test Fixtures"; - sourceTree = ""; - }; F04700000000000000000010 /* Forwarding Test Support */ = { isa = PBXGroup; children = ( @@ -881,7 +871,6 @@ BA4A4EAD7B8B3A380C9ED1C2 /* FrameworkTests */, A102A7EA2C3532D7FA88B0A4 /* TFITests */, F04700000000000000000010 /* Forwarding Test Support */, - DB9666953064BA9A005FB585 /* Persistence Test Fixtures */, ); sourceTree = ""; }; @@ -1449,7 +1438,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - E28A00000000000000000002 /* V28.store in Resources */, ); runOnlyForDeploymentPostprocessing = 0; };