Skip to content

Commit a63c497

Browse files
authored
fix: prevent connection data loss on normal app quit (#453)
* fix: prevent connection data loss on normal app quit (#452) * fix: add CHANGELOG entry and regression test for #452
1 parent 180c0fb commit a63c497

File tree

7 files changed

+64
-12
lines changed

7 files changed

+64
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1717

1818
### Fixed
1919

20+
- Saved connections disappearing after normal app quit (Cmd+Q) while persisting after force quit (#452)
2021
- Detail pane showing truncated values for LONGTEXT/MEDIUMTEXT/CLOB columns, preventing correct editing
2122
- Redis hash/list/set/zset/stream views showing empty or misaligned rows when values contained binary, null, or integer types
2223

TablePro/AppDelegate.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
116116
}
117117

118118
func applicationWillTerminate(_ notification: Notification) {
119+
UserDefaults.standard.synchronize()
119120
SSHTunnelManager.shared.terminateAllProcessesSync()
120121
}
121122

TablePro/Models/Connection/DatabaseConnection.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -500,10 +500,10 @@ struct DatabaseConnection: Identifiable, Hashable {
500500
}
501501
}
502502

503-
// MARK: - Sample Data for Development
503+
// MARK: - Preview Data
504504

505505
extension DatabaseConnection {
506-
static let sampleConnections: [DatabaseConnection] = []
506+
static let preview = DatabaseConnection(name: "Preview Connection")
507507
}
508508

509509
// MARK: - Codable Conformance

TablePro/Views/Connection/ConnectionFormView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1754,5 +1754,5 @@ private struct StartupCommandsEditor: NSViewRepresentable {
17541754
}
17551755

17561756
#Preview("Edit Connection") {
1757-
ConnectionFormView(connectionId: DatabaseConnection.sampleConnections[0].id)
1757+
ConnectionFormView(connectionId: DatabaseConnection.preview.id)
17581758
}

TablePro/Views/Connection/WelcomeWindowView.swift

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -728,13 +728,7 @@ struct WelcomeWindowView: View {
728728
// MARK: - Actions
729729

730730
private func loadConnections() {
731-
let saved = storage.loadConnections()
732-
if saved.isEmpty {
733-
connections = DatabaseConnection.sampleConnections
734-
storage.saveConnections(connections)
735-
} else {
736-
connections = saved
737-
}
731+
connections = storage.loadConnections()
738732
loadGroups()
739733
}
740734

TablePro/Views/Main/MainContentView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,11 +1067,11 @@ private struct FocusedCommandActionsModifier: ViewModifier {
10671067

10681068
#Preview("With Connection") {
10691069
let state = SessionStateFactory.create(
1070-
connection: DatabaseConnection.sampleConnections[0],
1070+
connection: DatabaseConnection.preview,
10711071
payload: nil
10721072
)
10731073
MainContentView(
1074-
connection: DatabaseConnection.sampleConnections[0],
1074+
connection: DatabaseConnection.preview,
10751075
payload: nil,
10761076
windowTitle: .constant("SQL Query"),
10771077
tables: .constant([]),
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//
2+
// ConnectionStoragePersistenceTests.swift
3+
// TableProTests
4+
//
5+
6+
import Foundation
7+
import Testing
8+
@testable import TablePro
9+
10+
@Suite("ConnectionStorage Persistence", .serialized)
11+
struct ConnectionStoragePersistenceTests {
12+
private let storage = ConnectionStorage.shared
13+
14+
@Test("loading empty storage does not write back to UserDefaults")
15+
func loadEmptyDoesNotWrite() {
16+
let original = storage.loadConnections()
17+
defer { storage.saveConnections(original) }
18+
19+
// Clear all connections
20+
storage.saveConnections([])
21+
22+
// Force cache clear by saving then loading
23+
let loaded = storage.loadConnections()
24+
#expect(loaded.isEmpty)
25+
26+
// Add a connection directly, bypassing cache
27+
let connection = DatabaseConnection(name: "Persistence Test")
28+
storage.addConnection(connection)
29+
defer { storage.deleteConnection(connection) }
30+
31+
// Loading again should return the connection, not overwrite with empty
32+
let reloaded = storage.loadConnections()
33+
#expect(reloaded.contains { $0.id == connection.id })
34+
}
35+
36+
@Test("round-trip save and load preserves connections")
37+
func roundTripSaveLoad() {
38+
let original = storage.loadConnections()
39+
defer { storage.saveConnections(original) }
40+
41+
let connection = DatabaseConnection(
42+
name: "Round Trip Test",
43+
host: "127.0.0.1",
44+
port: 5432,
45+
type: .postgresql
46+
)
47+
48+
storage.saveConnections([connection])
49+
let loaded = storage.loadConnections()
50+
51+
#expect(loaded.count == 1)
52+
#expect(loaded.first?.id == connection.id)
53+
#expect(loaded.first?.name == "Round Trip Test")
54+
#expect(loaded.first?.host == "127.0.0.1")
55+
}
56+
}

0 commit comments

Comments
 (0)