// // FolderPureLogicTests.swift // oAITests // // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Copyright (C) 2026 Rune Olsen import Testing import Foundation @testable import Confab @Suite("Folder tree pure logic") struct FolderOrderedTreeTests { private func makeFolder(_ name: String, parentId: UUID? = nil) -> Folder { Folder(name: name, parentId: parentId) } @Test("Single root folder with no children") func singleRoot() { let work = makeFolder("Work") let result = Folder.orderedTree(from: [work]) #expect(result.map { $0.folder.id } == [work.id]) #expect(result.map(\.depth) == [0]) } @Test("Multiple top-level roots preserve their relative order") func multipleRootsPreserveOrder() { let work = makeFolder("Work") let personal = makeFolder("Personal") let result = Folder.orderedTree(from: [work, personal]) #expect(result.map { $0.folder.id } == [work.id, personal.id]) #expect(result.map(\.depth) == [0, 0]) } @Test("Parent and children interleave depth-first, not grouped by level") func parentChildInterleaveDepthFirst() { let work = makeFolder("Work") let projectA = makeFolder("Project A", parentId: work.id) let projectB = makeFolder("Project B", parentId: work.id) let personal = makeFolder("Personal") let result = Folder.orderedTree(from: [work, projectA, projectB, personal]) #expect(result.map { $0.folder.id } == [work.id, projectA.id, projectB.id, personal.id]) #expect(result.map(\.depth) == [0, 1, 1, 0]) } @Test("Deep nesting (4-5 levels) reports correct depths") func deepNestingDepths() { let l0 = makeFolder("L0") let l1 = makeFolder("L1", parentId: l0.id) let l2 = makeFolder("L2", parentId: l1.id) let l3 = makeFolder("L3", parentId: l2.id) let l4 = makeFolder("L4", parentId: l3.id) let result = Folder.orderedTree(from: [l0, l1, l2, l3, l4]) #expect(result.map(\.depth) == [0, 1, 2, 3, 4]) } @Test("Empty input produces an empty tree") func emptyInput() { #expect(Folder.orderedTree(from: []).isEmpty) } } @Suite("Folder.isDescendant") struct FolderIsDescendantTests { private func makeFolder(_ name: String, parentId: UUID? = nil) -> Folder { Folder(name: name, parentId: parentId) } @Test("A direct child is a descendant of its parent") func directChild() { let parent = makeFolder("Work") let child = makeFolder("Project A", parentId: parent.id) #expect(Folder.isDescendant(child.id, of: parent.id, in: [parent, child])) } @Test("A grandchild is a descendant of its grandparent") func grandchild() { let grandparent = makeFolder("Work") let parent = makeFolder("Project A", parentId: grandparent.id) let child = makeFolder("Sub-task", parentId: parent.id) #expect(Folder.isDescendant(child.id, of: grandparent.id, in: [grandparent, parent, child])) } @Test("An unrelated folder is not a descendant") func unrelatedFolder() { let work = makeFolder("Work") let personal = makeFolder("Personal") #expect(Folder.isDescendant(personal.id, of: work.id, in: [work, personal]) == false) } @Test("A folder is considered a descendant of itself (rejects self-drop)") func selfReference() { let work = makeFolder("Work") #expect(Folder.isDescendant(work.id, of: work.id, in: [work])) } @Test("A candidate ID absent from the folder list returns false without crashing") func candidateAbsent() { let work = makeFolder("Work") #expect(Folder.isDescendant(UUID(), of: work.id, in: [work]) == false) } } @Suite("Folder.visibleFolderIds") struct FolderVisibleIdsTests { private func makeFolder(_ name: String, parentId: UUID? = nil) -> Folder { Folder(name: name, parentId: parentId) } @Test("No collapsed folders means everything is visible") func noCollapsedAllVisible() { let work = makeFolder("Work") let projectA = makeFolder("Project A", parentId: work.id) let tree = Folder.orderedTree(from: [work, projectA]) let visible = Folder.visibleFolderIds(tree: tree, collapsed: []) #expect(visible == Set([work.id, projectA.id])) } @Test("Collapsing a mid-depth folder hides all deeper descendants but keeps its own id visible") func collapsingMidDepthHidesDescendants() { let work = makeFolder("Work") let projectA = makeFolder("Project A", parentId: work.id) let subTask = makeFolder("Sub-task", parentId: projectA.id) let tree = Folder.orderedTree(from: [work, projectA, subTask]) let visible = Folder.visibleFolderIds(tree: tree, collapsed: [projectA.id]) #expect(visible == Set([work.id, projectA.id])) #expect(!visible.contains(subTask.id)) } @Test("Two unrelated collapsed folders hide their own subtrees independently") func independentCollapsedSubtrees() { let work = makeFolder("Work") let projectA = makeFolder("Project A", parentId: work.id) let personal = makeFolder("Personal") let hobby = makeFolder("Hobby", parentId: personal.id) let tree = Folder.orderedTree(from: [work, projectA, personal, hobby]) let visible = Folder.visibleFolderIds(tree: tree, collapsed: [work.id, personal.id]) #expect(visible == Set([work.id, personal.id])) } } @Suite("DraggedItem") struct DraggedItemTests { @Test("A single conversation round-trips through encode/decode") func singleConversationRoundTrip() { let id = UUID() let item = DraggedItem.conversations([id]) #expect(DraggedItem(rawValue: item.rawValue) == item) } @Test("Multiple bundled conversations round-trip through encode/decode") func multipleConversationsRoundTrip() { let ids = [UUID(), UUID(), UUID()] let item = DraggedItem.conversations(ids) guard case .conversations(let decoded) = DraggedItem(rawValue: item.rawValue) else { Issue.record("Expected .conversations case") return } #expect(decoded == ids) } @Test("A folder round-trips through encode/decode") func folderRoundTrip() { let id = UUID() let item = DraggedItem.folder(id) #expect(DraggedItem(rawValue: item.rawValue) == item) } @Test("A garbage string decodes to nil") func garbageStringIsNil() { #expect(DraggedItem(rawValue: "not-a-valid-payload") == nil) } }