Phase 4 seam: in-memory DatabaseService + DB injection for ContextSelectionService

DatabaseService gains a testable init(dbQueue:) plus a makeInMemory()
convenience and schema-introspection helpers (tableExists/columnNames),
so migration tests don't need the test target to import GRDB directly.
ContextSelectionService now takes an injected DatabaseService (defaults
to .shared) and its DB-coupled methods (smartSelection,
getSummariesForExcludedRange, isMessageStarred) are bumped to internal.
No logic changes to existing call sites.
This commit is contained in:
2026-07-24 09:16:42 +02:00
parent 02bf73fec6
commit c6a7439648
2 changed files with 36 additions and 11 deletions
+11 -6
View File
@@ -47,7 +47,12 @@ enum SelectionStrategy {
final class ContextSelectionService {
static let shared = ContextSelectionService()
private init() {}
private let db: DatabaseService
/// `db` defaults to the shared on-disk instance; tests inject an in-memory `DatabaseService`.
init(db: DatabaseService = .shared) {
self.db = db
}
/// Select context messages using the specified strategy
func selectContext(
@@ -98,7 +103,7 @@ final class ContextSelectionService {
// MARK: - Smart Selection Algorithm
private func smartSelection(allMessages: [Message], maxTokens: Int, conversationId: UUID? = nil) -> ContextWindow {
func smartSelection(allMessages: [Message], maxTokens: Int, conversationId: UUID? = nil) -> ContextWindow {
guard !allMessages.isEmpty else {
return ContextWindow(messages: [], summaries: [], totalTokens: 0, excludedCount: 0)
}
@@ -191,12 +196,12 @@ final class ContextSelectionService {
}
/// Get summaries for excluded message ranges
private func getSummariesForExcludedRange(
func getSummariesForExcludedRange(
conversationId: UUID,
totalMessages: Int,
selectedCount: Int
) -> [String] {
guard let summaryRecords = try? DatabaseService.shared.getConversationSummaries(conversationId: conversationId) else {
guard let summaryRecords = try? db.getConversationSummaries(conversationId: conversationId) else {
return []
}
@@ -238,8 +243,8 @@ final class ContextSelectionService {
}
/// Check if a message is starred by the user
private func isMessageStarred(_ message: Message) -> Bool {
guard let metadata = try? DatabaseService.shared.getMessageMetadata(messageId: message.id) else {
func isMessageStarred(_ message: Message) -> Bool {
guard let metadata = try? db.getMessageMetadata(messageId: message.id) else {
return false
}
return metadata.user_starred == 1