new tests

This commit is contained in:
Mark Gregory
2026-10-19 10:32:15 +01:00
parent 6251f2490b
commit f46f598baf
2 changed files with 200 additions and 26 deletions
+63 -12
View File
@@ -17,27 +17,54 @@ struct QueryResult {
}
enum PasswordStore {
static func read(_ id: UUID) -> String {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "WorkbenchReplacement.MySQL",
kSecAttrAccount as String: id.uuidString,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
private static let service = "DBSyn.MySQL"
private static let legacyService = "WorkbenchReplacement.MySQL"
private static func lookup(_ id: UUID, service: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: id.uuidString,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var value: CFTypeRef?
guard SecItemCopyMatching(query as CFDictionary, &value) == errSecSuccess,
let data = value as? Data else { return "" }
return String(data: data, encoding: .utf8) ?? ""
let data = value as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
static func read(_ id: UUID) -> String {
if let password = lookup(id, service: service) { return password }
// One-time compatibility read. Never delete the legacy item.
if let password = lookup(id, service: legacyService) {
save(password, id: id)
return password
}
return ""
}
static func save(_ password: String, id: UUID) {
let query: [String: Any] = [kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "WorkbenchReplacement.MySQL",
kSecAttrAccount as String: id.uuidString]
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: id.uuidString
]
SecItemDelete(query as CFDictionary)
guard !password.isEmpty else { return }
var attributes = query
attributes[kSecValueData as String] = Data(password.utf8)
SecItemAdd(attributes as CFDictionary, nil)
}
static func delete(_ id: UUID) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: id.uuidString
]
SecItemDelete(query as CFDictionary)
}
}
@@ -507,7 +534,8 @@ final class DatabaseModel: ObservableObject {
let cached = try? JSONDecoder().decode([String: SchemaIntrospection].self, from: data) {
introspections = cached.filter { entry in profiles.contains(where: { $0.id == entry.value.connectionID }) }
}
selectedID = profiles.first?.id
let rememberedID = UserDefaults.standard.string(forKey: "DBSyn.selectedConnectionID")
selectedID = profiles.first(where: { $0.id.uuidString == rememberedID })?.id ?? profiles.first?.id
if let id = selectedID {
for snapshot in introspections.values where snapshot.connectionID == id {
applyIntrospection(snapshot)
@@ -620,11 +648,34 @@ final class DatabaseModel: ObservableObject {
}
var selected: ConnectionProfile? { profiles.first { $0.id == selectedID } }
func selectConnection(_ id: UUID) {
guard let profile = profiles.first(where: { $0.id == id }) else { return }
saveActiveQuery()
selectedID = id
UserDefaults.standard.set(id.uuidString, forKey: "DBSyn.selectedConnectionID")
selectedDatabase = profile.database
databases = []
status = "Selected connection: \(profile.name)"
loadDatabases()
}
func deleteConnection(_ id: UUID) {
guard profiles.count > 1 else { return }
NativeMySQLRunner.disconnect(profileID: id)
PasswordStore.delete(id)
profiles.removeAll { $0.id == id }
if selectedID == id, let first = profiles.first { selectConnection(first.id) }
persist()
}
func save(_ profile: ConnectionProfile, password: String) {
if let i = profiles.firstIndex(where: { $0.id == profile.id }) { profiles[i] = profile }
else { profiles.append(profile) }
PasswordStore.save(password, id: profile.id)
selectedID = profile.id
selectedDatabase = profile.database
UserDefaults.standard.set(profile.id.uuidString, forKey: "DBSyn.selectedConnectionID")
persist()
}
private func persist() {