Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

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

Expand Down
1 change: 1 addition & 0 deletions TablePro/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationWillTerminate(_ notification: Notification) {
UserDefaults.standard.synchronize()
SSHTunnelManager.shared.terminateAllProcessesSync()
}

Expand Down
4 changes: 2 additions & 2 deletions TablePro/Models/Connection/DatabaseConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -500,10 +500,10 @@ struct DatabaseConnection: Identifiable, Hashable {
}
}

// MARK: - Sample Data for Development
// MARK: - Preview Data

extension DatabaseConnection {
static let sampleConnections: [DatabaseConnection] = []
static let preview = DatabaseConnection(name: "Preview Connection")
}

// MARK: - Codable Conformance
Expand Down
2 changes: 1 addition & 1 deletion TablePro/Views/Connection/ConnectionFormView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1754,5 +1754,5 @@ private struct StartupCommandsEditor: NSViewRepresentable {
}

#Preview("Edit Connection") {
ConnectionFormView(connectionId: DatabaseConnection.sampleConnections[0].id)
ConnectionFormView(connectionId: DatabaseConnection.preview.id)
}
8 changes: 1 addition & 7 deletions TablePro/Views/Connection/WelcomeWindowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -728,13 +728,7 @@ struct WelcomeWindowView: View {
// MARK: - Actions

private func loadConnections() {
let saved = storage.loadConnections()
if saved.isEmpty {
connections = DatabaseConnection.sampleConnections
storage.saveConnections(connections)
} else {
connections = saved
}
connections = storage.loadConnections()
loadGroups()
}

Expand Down
4 changes: 2 additions & 2 deletions TablePro/Views/Main/MainContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1067,11 +1067,11 @@ private struct FocusedCommandActionsModifier: ViewModifier {

#Preview("With Connection") {
let state = SessionStateFactory.create(
connection: DatabaseConnection.sampleConnections[0],
connection: DatabaseConnection.preview,
payload: nil
)
MainContentView(
connection: DatabaseConnection.sampleConnections[0],
connection: DatabaseConnection.preview,
payload: nil,
windowTitle: .constant("SQL Query"),
tables: .constant([]),
Expand Down
56 changes: 56 additions & 0 deletions TableProTests/Core/Storage/ConnectionStoragePersistenceTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// ConnectionStoragePersistenceTests.swift
// TableProTests
//

import Foundation
import Testing
@testable import TablePro

@Suite("ConnectionStorage Persistence", .serialized)
struct ConnectionStoragePersistenceTests {
private let storage = ConnectionStorage.shared

@Test("loading empty storage does not write back to UserDefaults")
func loadEmptyDoesNotWrite() {
let original = storage.loadConnections()
defer { storage.saveConnections(original) }

// Clear all connections
storage.saveConnections([])

// Force cache clear by saving then loading
let loaded = storage.loadConnections()
#expect(loaded.isEmpty)

// Add a connection directly, bypassing cache
let connection = DatabaseConnection(name: "Persistence Test")
storage.addConnection(connection)
defer { storage.deleteConnection(connection) }

// Loading again should return the connection, not overwrite with empty
let reloaded = storage.loadConnections()
#expect(reloaded.contains { $0.id == connection.id })
}

@Test("round-trip save and load preserves connections")
func roundTripSaveLoad() {
let original = storage.loadConnections()
defer { storage.saveConnections(original) }

let connection = DatabaseConnection(
name: "Round Trip Test",
host: "127.0.0.1",
port: 5432,
type: .postgresql
)

storage.saveConnections([connection])
let loaded = storage.loadConnections()

#expect(loaded.count == 1)
#expect(loaded.first?.id == connection.id)
#expect(loaded.first?.name == "Round Trip Test")
#expect(loaded.first?.host == "127.0.0.1")
}
}
Loading