import XCTest @testable import CommandNotch @MainActor final class WorkspaceRegistryTests: XCTestCase { func testRegistryCreatesDefaultWorkspaceWhenEmpty() { let registry = makeRegistry() XCTAssertEqual(registry.allWorkspaceSummaries().count, 1) XCTAssertEqual(registry.allWorkspaceSummaries().first?.name, "Main") XCTAssertNotNil(registry.controller(for: registry.defaultWorkspaceID)) } func testCreateRenameAndDeleteWorkspaceUpdatesSummaries() { let registry = makeRegistry() let createdID = registry.createWorkspace(named: "Docs") XCTAssertEqual(registry.allWorkspaceSummaries().map(\.name), ["Main", "Docs"]) registry.renameWorkspace(id: createdID, to: "Review") XCTAssertEqual(registry.allWorkspaceSummaries().map(\.name), ["Main", "Review"]) registry.deleteWorkspace(id: createdID) XCTAssertEqual(registry.allWorkspaceSummaries().map(\.name), ["Main"]) } func testDeletingLastWorkspaceIsIgnored() { let registry = makeRegistry() let onlyWorkspaceID = registry.defaultWorkspaceID registry.deleteWorkspace(id: onlyWorkspaceID) XCTAssertEqual(registry.allWorkspaceSummaries().count, 1) XCTAssertEqual(registry.allWorkspaceSummaries().first?.id, onlyWorkspaceID) } func testDeletionFallbackPrefersRequestedWorkspaceWhenAvailable() { let registry = makeRegistry() let docsID = registry.createWorkspace(named: "Docs") let reviewID = registry.createWorkspace(named: "Review") let fallback = registry.deletionFallbackWorkspaceID( forDeleting: docsID, preferredFallback: reviewID ) XCTAssertEqual(fallback, reviewID) } func testRegistryLoadsPersistedWorkspacesFromStore() { let store = InMemoryWorkspaceStore() let docsID = UUID(uuidString: "11111111-1111-1111-1111-111111111111")! store.savedSummaries = [WorkspaceSummary(id: docsID, name: "Docs")] let registry = makeRegistry(initialWorkspaces: nil, store: store) XCTAssertEqual(registry.allWorkspaceSummaries().count, 1) XCTAssertEqual(registry.allWorkspaceSummaries().first?.id, docsID) XCTAssertEqual(registry.allWorkspaceSummaries().first?.name, "Docs") XCTAssertEqual(registry.defaultWorkspaceID, docsID) } func testRegistryPersistsWorkspaceSummaryChanges() { let store = InMemoryWorkspaceStore() let registry = makeRegistry(store: store) let createdID = registry.createWorkspace(named: "Docs") XCTAssertEqual(store.savedSummaries.map(\.name), ["Main", "Docs"]) registry.renameWorkspace(id: createdID, to: "Review") XCTAssertEqual(store.savedSummaries.map(\.name), ["Main", "Review"]) registry.deleteWorkspace(id: createdID) XCTAssertEqual(store.savedSummaries.map(\.name), ["Main"]) } func testUpdateWorkspaceHotkeyPersistsAndUpdatesSummary() { let store = InMemoryWorkspaceStore() let registry = makeRegistry(store: store) let docsID = registry.createWorkspace(named: "Docs") let hotkey = HotkeyBinding.cmdShiftDigit(4) registry.updateWorkspaceHotkey(id: docsID, to: hotkey) XCTAssertEqual(registry.summary(for: docsID)?.hotkey, hotkey) XCTAssertEqual(store.savedSummaries.last?.hotkey, hotkey) } func testNextAndPreviousWorkspaceWrapAroundRegistryOrder() { let registry = makeRegistry() let mainID = registry.defaultWorkspaceID let docsID = registry.createWorkspace(named: "Docs") let reviewID = registry.createWorkspace(named: "Review") XCTAssertEqual(registry.nextWorkspaceID(after: mainID), docsID) XCTAssertEqual(registry.nextWorkspaceID(after: reviewID), mainID) XCTAssertEqual(registry.previousWorkspaceID(before: mainID), reviewID) XCTAssertEqual(registry.previousWorkspaceID(before: docsID), mainID) } private func makeRegistry( initialWorkspaces: [WorkspaceSummary]? = [], store: (any WorkspaceStoreType)? = nil ) -> WorkspaceRegistry { let settingsProvider = TestSettingsProvider() let sessionFactory = UnusedTerminalSessionFactory() return WorkspaceRegistry( initialWorkspaces: initialWorkspaces, store: store, controllerFactory: { summary in WorkspaceController( summary: summary, sessionFactory: sessionFactory, settingsProvider: settingsProvider, bootstrapDefaultTab: false ) } ) } } private final class InMemoryWorkspaceStore: WorkspaceStoreType { var savedSummaries: [WorkspaceSummary] = [] func loadWorkspaceSummaries() -> [WorkspaceSummary] { savedSummaries } func saveWorkspaceSummaries(_ summaries: [WorkspaceSummary]) { savedSummaries = summaries } } private final class TestSettingsProvider: TerminalSessionConfigurationProviding { let terminalSessionConfiguration = TerminalSessionConfiguration(fontSize: 13, theme: .terminalApp, shellPath: "") let hotkeySettings = AppSettings.default.hotkeys let terminalSizePresets = TerminalSizePresetStore.loadDefaults() } private struct UnusedTerminalSessionFactory: TerminalSessionFactoryType { @MainActor func makeSession(fontSize: CGFloat, theme: TerminalTheme, shellPath: String) -> TerminalSession { fatalError("WorkspaceRegistryTests should not create live terminal sessions.") } }