Skip to content

refactor: Apple-pattern storage DI plus 3 product bug fixes#955

Merged
datlechin merged 29 commits intomainfrom
fix/ssh-storage-dialect-and-tests
Apr 30, 2026
Merged

refactor: Apple-pattern storage DI plus 3 product bug fixes#955
datlechin merged 29 commits intomainfrom
fix/ssh-storage-dialect-and-tests

Conversation

@datlechin
Copy link
Copy Markdown
Member

@datlechin datlechin commented Apr 30, 2026

Summary

Three product bug fixes plus a storage singleton refactor to Apple's URLSession.shared-style dependency injection pattern, enabling parallel test execution without races. Surfaced during a deep audit of pre-existing test failures and an end-to-end review of the External API code path.

Product fixes

  • SSH agent socket tildeLibSSH2TunnelFactory.buildAuthenticator now expands ~ in config.agentSocketPath and configEntry?.identityAgent before handing them to AgentAuthenticator. libssh2 does not expand ~, so users on .sshAgent auth with a custom socket path (1Password and similar) silently failed to connect. HIGH severity.
  • GroupStorage delete sync orderingdeleteGroup now persists before firing the sync notification, mirroring the precedent fixed in ConnectionStorage. Previously, markDeleted could trigger an iCloud sync that read stale data and re-uploaded the deleted group. MEDIUM severity.
  • Dialect resolver throws on registry missSQLStatementGenerator, SQLRowToStatementConverter, QueryTabManager, QueryTab.buildBaseTableQuery, and DataChangeManager now throw SQLDialectError.dialectUnavailable when PluginMetadataRegistry.snapshot(forTypeId:) returns nil. Previously fell back to identity quoting, silently emitting unquoted identifiers for tables/columns with reserved words. Throw is absorbed at UI boundaries via do/catch with os.Logger.

Singleton refactor (Apple pattern)

Nine classes refactored to Apple's documented pattern (WWDC 2018 #417, mirrors URLSession.shared + URLSession(configuration:)):

  • Storage: GroupStorage, AppSettingsStorage, ConnectionStorage, SyncMetadataStorage, QueryHistoryStorage, SQLFavoriteStorage, SQLFavoriteManager
  • Database / Plugin: DatabaseManager, PluginManager
  • Sync: SyncChangeTracker (now injects SyncMetadataStorage so test-isolated chains stay isolated end-to-end)

Each class keeps static let shared for production callers (435+ references unchanged). Adds public init accepting dependencies via parameters with defaults. Tests construct isolated instances using UserDefaults(suiteName: UUID().uuidString) or temp file URLs.

Result: parallel test execution re-enabled (parallelizable="YES" restored on the scheme). Inherently shared-state tests marked with @Suite(.serialized) per Apple's published guidance; tests previously serialized for race protection now run in parallel because their dependencies are isolated.

Removed: init(isolatedForTesting: Bool) and init(isolatedStorage:) flavors on QueryHistoryStorage, SQLFavoriteStorage, SQLFavoriteManager. Replaced by Apple-pattern inits taking the actual dependency. No backward-compat shim.

Known regression risk to verify: SQLFavoriteStorage.init now waits synchronously on database setup at first .shared access. Prior init was async. Smoke-test app launch time before merging — if cold-launch slows visibly, defer database open via lazy property.

Test cleanup

  • Delete 329 lines of dead LIMIT-1 / TOP(1) safety guard tests across three SQLStatementGenerator*Tests files (feature was removed from product code).
  • Update Cassandra icon expectation to cassandra-icon and safe-mode lock symbol to lock.open.fill (renamed in product).
  • Add FakeMSSQLPlugin stub to test target so TableQueryBuilderMSSQLTests can resolve MSSQL dialect/driver.

Test plan

  • Run TablePro test suite under parallel execution, confirm previously failing tests now pass and no new races
  • SSH agent connection with ~-prefixed socket path connects successfully (1Password)
  • Delete a connection group with iCloud sync enabled, confirm group does not reappear after sync
  • Open a table with a reserved-word column name on a connection whose plugin has not finished loading: expect a logged error rather than silent malformed SQL
  • Verify production behavior unchanged for callers of .shared (default args preserve the singleton path)
  • Cold-launch timing: open the app fresh, verify no perceptible delay vs current main (see SQLFavoriteStorage note above)

Related

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: be69ff2b04

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +124 to +127
} catch {
MainContentView.lifecycleLogger.error(
"[open] buildBaseTableQuery failed for restored tab table=\(tableName, privacy: .public): \(error.localizedDescription, privacy: .public)"
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset table query after restore rebuild failure

When buildBaseTableQuery throws here (for example while plugin metadata/dialect is still unavailable), the catch block only logs and leaves the persisted content.query untouched. Later in handleRestoreOrDefault, table tabs are auto-executed whenever content.query is non-empty, so this path can still run stale persisted SQL instead of failing closed. That reintroduces malformed/outdated query execution during restore, which this change set is otherwise trying to prevent.

Useful? React with 👍 / 👎.

@datlechin datlechin changed the title fix: ssh agent tilde, group sync ordering, dialect throw, plus test cleanup refactor: Apple-pattern storage DI plus 3 product bug fixes Apr 30, 2026
@datlechin datlechin merged commit c9a17b1 into main Apr 30, 2026
2 checks passed
@datlechin datlechin deleted the fix/ssh-storage-dialect-and-tests branch April 30, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant