Add folders for organizing saved conversations
Conversations can now be filed into flat (non-nested) folders, shown as collapsible sections in both the sidebar and the advanced conversation list. New folders migration (v9) adds a folders table and conversations.folderId with ON DELETE SET NULL, so deleting a folder unfiles its conversations rather than losing them. Move/rename/ delete via context menu; conversation lists with no folders fall back to the existing flat view unchanged.
This commit is contained in:
@@ -220,3 +220,96 @@ struct DatabaseServiceUsageStatsTests {
|
||||
#expect(byConversation.count == 3)
|
||||
}
|
||||
}
|
||||
|
||||
@Suite("DatabaseService folders, against a throwaway in-memory queue")
|
||||
struct DatabaseServiceFolderTests {
|
||||
|
||||
@Test("v9 adds the folders table and folderId to conversations")
|
||||
func v9AddsFolderSupport() {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
#expect(db.tableExists("folders"))
|
||||
#expect(db.columnNames(in: "conversations").contains("folderId"))
|
||||
}
|
||||
|
||||
@Test("Creating folders assigns increasing sort order")
|
||||
func createFolderAssignsSortOrder() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let first = try db.createFolder(name: "Work")
|
||||
let second = try db.createFolder(name: "Personal")
|
||||
#expect(first.sortOrder == 0)
|
||||
#expect(second.sortOrder == 1)
|
||||
}
|
||||
|
||||
@Test("listFolders returns folders ordered by sortOrder")
|
||||
func listFoldersOrdered() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
_ = try db.createFolder(name: "Work")
|
||||
_ = try db.createFolder(name: "Personal")
|
||||
|
||||
let folders = try db.listFolders()
|
||||
#expect(folders.map(\.name) == ["Work", "Personal"])
|
||||
}
|
||||
|
||||
@Test("renameFolder updates the stored name")
|
||||
func renameFolderUpdatesName() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let folder = try db.createFolder(name: "Work")
|
||||
try db.renameFolder(id: folder.id, name: "Projects")
|
||||
|
||||
let folders = try db.listFolders()
|
||||
#expect(folders.first?.name == "Projects")
|
||||
}
|
||||
|
||||
@Test("moveConversation files a conversation into a folder")
|
||||
func moveConversationFiles() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let folder = try db.createFolder(name: "Work")
|
||||
let conversation = try db.saveConversation(name: "Chat", messages: [Message(role: .user, content: "hi")])
|
||||
|
||||
try db.moveConversation(id: conversation.id, toFolder: folder.id)
|
||||
|
||||
let loaded = try db.loadConversation(id: conversation.id)
|
||||
#expect(loaded?.0.folderId == folder.id)
|
||||
}
|
||||
|
||||
@Test("moveConversation with nil folder unfiles a conversation")
|
||||
func moveConversationUnfiles() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let folder = try db.createFolder(name: "Work")
|
||||
let conversation = try db.saveConversation(name: "Chat", messages: [Message(role: .user, content: "hi")])
|
||||
try db.moveConversation(id: conversation.id, toFolder: folder.id)
|
||||
|
||||
try db.moveConversation(id: conversation.id, toFolder: nil)
|
||||
|
||||
let loaded = try db.loadConversation(id: conversation.id)
|
||||
#expect(loaded?.0.folderId == nil)
|
||||
}
|
||||
|
||||
@Test("Deleting a folder unfiles its conversations instead of deleting them")
|
||||
func deleteFolderUnfilesConversations() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let folder = try db.createFolder(name: "Work")
|
||||
let conversation = try db.saveConversation(name: "Chat", messages: [Message(role: .user, content: "hi")])
|
||||
try db.moveConversation(id: conversation.id, toFolder: folder.id)
|
||||
|
||||
try db.deleteFolder(id: folder.id)
|
||||
|
||||
let folders = try db.listFolders()
|
||||
#expect(folders.isEmpty)
|
||||
|
||||
let loaded = try db.loadConversation(id: conversation.id)
|
||||
#expect(loaded != nil)
|
||||
#expect(loaded?.0.folderId == nil)
|
||||
}
|
||||
|
||||
@Test("listConversations reflects folderId")
|
||||
func listConversationsReflectsFolderId() throws {
|
||||
let db = DatabaseService.makeInMemory()
|
||||
let folder = try db.createFolder(name: "Work")
|
||||
let conversation = try db.saveConversation(name: "Chat", messages: [Message(role: .user, content: "hi")])
|
||||
try db.moveConversation(id: conversation.id, toFolder: folder.id)
|
||||
|
||||
let conversations = try db.listConversations()
|
||||
#expect(conversations.first?.folderId == folder.id)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user