From b5bdac41c06fe8dfc4783e5a776066e4d512b30a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 8 May 2026 14:10:40 +0700 Subject: [PATCH 1/3] test(ai-chat): resolve currentQuery via resolveTurnForWire instead of asserting on raw turn --- .../ViewModels/AIChatViewModelMentionsTests.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/TableProTests/ViewModels/AIChatViewModelMentionsTests.swift b/TableProTests/ViewModels/AIChatViewModelMentionsTests.swift index 4469eb186..e81fe1515 100644 --- a/TableProTests/ViewModels/AIChatViewModelMentionsTests.swift +++ b/TableProTests/ViewModels/AIChatViewModelMentionsTests.swift @@ -58,8 +58,8 @@ struct AIChatViewModelMentionsTests { #expect(vm.attachedContext.isEmpty) } - @Test("Sending with currentQuery attachment embeds the query text in the prompt") - func currentQueryResolved() { + @Test("Sending with currentQuery attachment resolves the query text on the wire") + func currentQueryResolved() async { let vm = AIChatViewModel() vm.connection = TestFixtures.makeConnection(type: .mysql) vm.inputText = "Explain" @@ -67,8 +67,12 @@ struct AIChatViewModelMentionsTests { vm.sendMessage() - let userTurn = vm.messages.first(where: { $0.role == .user }) - let prompt = userTurn?.plainText ?? "" + guard let userTurn = vm.messages.first(where: { $0.role == .user }) else { + Issue.record("expected a user turn after sendMessage") + return + } + let wire = await vm.resolveTurnForWire(userTurn) + let prompt = wire.plainText #expect(prompt.contains("Explain")) #expect(prompt.contains("SELECT * FROM Customer")) #expect(prompt.contains("## Current Query")) From 0e2b0aecbfedcddbd2728ee8919d54512b08519d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 8 May 2026 14:10:41 +0700 Subject: [PATCH 2/3] test(coordinator): seed lazy-load skip path with execution timestamp and registry eviction --- .../Views/Main/MainContentCoordinatorLazyLoadTests.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TableProTests/Views/Main/MainContentCoordinatorLazyLoadTests.swift b/TableProTests/Views/Main/MainContentCoordinatorLazyLoadTests.swift index 96a770e52..59f56c3d8 100644 --- a/TableProTests/Views/Main/MainContentCoordinatorLazyLoadTests.swift +++ b/TableProTests/Views/Main/MainContentCoordinatorLazyLoadTests.swift @@ -140,11 +140,14 @@ struct MainContentCoordinatorLazyLoadTests { func skipsWhenAlreadyExecuting() { let (coordinator, tabManager) = makeCoordinator() let tabId = addTableTab(to: tabManager) + seedRows(coordinator, for: tabId, rowCount: 1) guard let idx = tabManager.tabs.firstIndex(where: { $0.id == tabId }) else { Issue.record("expected tab to exist") return } + tabManager.tabs[idx].execution.lastExecutedAt = Date() tabManager.tabs[idx].execution.isExecuting = true + coordinator.tabSessionRegistry.evict(for: tabId) coordinator.lazyLoadCurrentTabIfNeeded() #expect(coordinator.needsLazyLoad == false) From 59ac47bfdc8b9145d3d69304065837e5e5603aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ng=C3=B4=20Qu=E1=BB=91c=20=C4=90=E1=BA=A1t?= Date: Fri, 8 May 2026 14:10:41 +0700 Subject: [PATCH 3/3] fix(session): do not auto-create empty query tab for content-less query payloads --- CHANGELOG.md | 1 + .../Infrastructure/SessionStateFactory.swift | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bde46673..a3964c25b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - AI Chat: `@` mention detection no longer breaks when the cursor sits right after an emoji or other non-BMP character - AI Chat: Fix Error prompt now reads "MongoDB query" and "Redis command" using the database display name, instead of the raw query language label - Internal: tab session registry binds automatically when a coordinator falls back to creating its own registry, so unit tests no longer trip the filter-state debug assertion +- Connection-only payloads no longer create an empty `Query 1` tab when there is no query, title, or source file to populate it ## [0.39.1] - 2026-05-08 diff --git a/TablePro/Core/Services/Infrastructure/SessionStateFactory.swift b/TablePro/Core/Services/Infrastructure/SessionStateFactory.swift index 85cfd8bf1..33956d6d2 100644 --- a/TablePro/Core/Services/Infrastructure/SessionStateFactory.swift +++ b/TablePro/Core/Services/Infrastructure/SessionStateFactory.swift @@ -123,12 +123,17 @@ enum SessionStateFactory { tabMgr.addTab(databaseName: payload.databaseName ?? activeDatabaseName) } case .query: - tabMgr.addTab( - initialQuery: payload.initialQuery, - title: payload.tabTitle, - databaseName: payload.databaseName ?? activeDatabaseName, - sourceFileURL: payload.sourceFileURL - ) + let hasContent = payload.initialQuery != nil + || payload.tabTitle != nil + || payload.sourceFileURL != nil + if hasContent { + tabMgr.addTab( + initialQuery: payload.initialQuery, + title: payload.tabTitle, + databaseName: payload.databaseName ?? activeDatabaseName, + sourceFileURL: payload.sourceFileURL + ) + } case .createTable: tabMgr.addCreateTableTab( databaseName: payload.databaseName ?? activeDatabaseName