diff --git a/Memory/ARCHITECTURE.md b/Memory/ARCHITECTURE.md index e4193696..1d689953 100644 --- a/Memory/ARCHITECTURE.md +++ b/Memory/ARCHITECTURE.md @@ -42,7 +42,10 @@ and bundle-derived Keychain access group, with target-specific signing. Shared localized resources are copied into each app bundle. Platform icons and metadata remain app-owned. Framework, macOS, and mobile tests stay separate; architecture tests fail closed when an expected production directory is absent or -empty. +empty. Source-based checks are limited to module dependencies, native-list import +allowlists, AgentKit type ownership, and SwiftData model ownership. They do not pin +UI copy, source layout, call-site spellings, retired paths, or shared-component wiring; +those concerns rely on behavioral tests or explicit visual review. ## State, navigation, and ownership @@ -623,7 +626,6 @@ Presentation owns the shared command-history subtitle, Vault sync status label, and the exported SFTP drag type. App targets retain native card/list composition, selection, menus, and clipboard behavior. - ### Assistant transcript presentation parity (2026-09-12) TermindPresentation owns tool summaries, batch status semantics, bounded result previews, timestamp formatting, diff --git a/Memory/REFACTOR.md b/Memory/REFACTOR.md index 7f3c4667..6abc00ef 100644 --- a/Memory/REFACTOR.md +++ b/Memory/REFACTOR.md @@ -118,3 +118,13 @@ Verification and remaining limitations (2026-09-16): - The full TFI action did not build or execute tests: the supplied simulator destination UUID was unavailable. This is not a passing iOS verification. A fresh device listing found an available booted iPhone 17 Pro, but the owner requested an immediate commit before rerunning either platform. - Strict Swift formatting across all production/test targets, `Scripts/check-zh-localization.sh`, the removed desktop-helper reference scan, and `git diff --check` passed. Xcode successfully resolved modules that standalone SourceKit could not find. Existing generic SwiftLint file/type/function-size and complexity findings remain outside this independent refactor; no inline suppressions were added. Dependencies and localization catalogs are unchanged. - Submitted at the owner's explicit request with these verification limitations retained rather than deleting unrelated behavioral tests to obtain a green result. + +## Remove Low-Value UI and Source Assertions (2026-09-16) + +- **Implemented; full verification in progress.** Evidence: `rg -n '#filePath|sourceRoot|String\(contentsOf|source\.contains' FrameworkTests TFMTests TFITests -g '*.swift'` found 35 architecture tests plus eight source snapshots in Color System, Menu Symbol, SSH Import, Keychain Order, Commerce Configuration, and Mobile Navigation Parity. At the owner's request, removed implementation-spelling/layout snapshots, enum and palette echoes, fixed-canvas render checks, disconnected standard-library sorting tests, and orphaned helpers; trimmed literal copy from mixed behavioral tests. Removed 52 test declarations and a net 1,230 Swift lines against an estimated 1,000–1,200-line reduction. Source assertions dropped from 43 tests to nine narrow module/dependency and ownership guards. The ListViewKit import allowlist now permits removing an importer without requiring a snapshot update, while still rejecting new unapproved importers. Retained language/catalog validation, protocol/data contracts, behavioral lifecycle/rendering coverage, and opt-in screenshot capture. Grouped host-list coverage now checks row placement using fixture data rather than localized headings. No production, dependency, or localization catalog changed; existing architecture-document edits were preserved. + +Verification in progress: + +- Initial pinned Xcode checks passed all 18 selected macOS tests and five selected iOS tests, compiling both test targets. Results: `/tmp/termind-test-cleanup-initial-macos.xcresult` and `/tmp/termind-test-cleanup-initial-ios.xcresult`. +- Strict Swift formatting across production/test targets, `Scripts/check-zh-localization.sh`, the remaining-source-assertion scan, and `git diff --check` passed. Active standalone SourceKit checks could not resolve the Xcode test module graph and were partly inconclusive; Xcode compilation is the authoritative check. Pre-existing generic SwiftLint size, tuple, pixel-coordinate naming, and force-try findings were left outside this deletion; no inline suppressions were added. +- Full pinned macOS and iOS test actions are running sequentially. No unrelated behavioral test is being removed to hide a failing result. diff --git a/TFITests/MobileAISettingsToolbarTests.swift b/TFITests/MobileAISettingsToolbarTests.swift index fe3e8a6b..55dee876 100644 --- a/TFITests/MobileAISettingsToolbarTests.swift +++ b/TFITests/MobileAISettingsToolbarTests.swift @@ -1,4 +1,3 @@ -import AgentKit import SwiftUI import TermindDomain import Testing @@ -8,59 +7,6 @@ import UIKit @Suite(.serialized) @MainActor struct MobileAISettingsToolbarTests { - @Test func managementPagesExposeTheirNativeToolbarActions() async throws { - let app = try MobileAppModel(inMemory: true) - - try await withNavigation(MobileModelsSettingsView(ai: app.ai)) { names in - #expect(names.contains("Add Provider")) - } - try await withNavigation(MobileSkillSettings(skills: app.skills)) { names in - #expect(names.contains("Add Skill")) - #expect(names.contains("More")) - #expect(!names.contains("Import Skill…")) - } - try await withNavigation(MobileMCPSettings(ai: app.ai, pro: app.pro)) { names in - #expect(names.contains("Add Server")) - } - } - - @Test func mcpServerUsesPushedEditorToolbar() async throws { - let app = try MobileAppModel(inMemory: true) - let server = MCPServer(name: "Test", url: "https://example.invalid/mcp") - - try await withNavigation(MobileMCPEditor(ai: app.ai, pro: app.pro, server: server)) { names in - #expect(names.contains("More")) - #expect(names.contains("Save")) - #expect(!names.contains("Cancel")) - } - } - - @Test func workspacePagesExposeTheirExpectedToolbarActions() async throws { - let app = try MobileAppModel(inMemory: true) - let host = TermindDomain.Host(name: "Test", host: "example.invalid") - app.library.upsert(host) - - for kind in [ConnectionHistoryEntry.Kind.ssh, .sftp] { - let workspace = MobileWorkspace( - descriptor: MobileWorkspaceDescriptor(hostID: host.id, title: host.name, kind: kind), - hosts: app.library) - try await withNavigation( - MobileWorkspaceView( - library: app.library, pro: app.pro, commandGenerator: app.commandGenerator, - workspace: workspace, workspaces: [workspace], - selectWorkspace: { _ in }, renameWorkspace: { _ in }, - openSameDirectory: { _ in }, closeWorkspace: { _ in }) - ) { names in - #expect(names.contains("More")) - #expect(!names.contains("History")) - #expect(!names.contains("Transfers")) - #expect(!names.contains("Close")) - #expect(!names.contains("Previous Location")) - #expect(!names.contains("Next Location")) - } - } - } - @Test func workspaceDetailHidesTheRootTabBar() async throws { let app = try MobileAppModel(inMemory: true) let host = TermindDomain.Host(name: "Test", host: "example.invalid") @@ -82,16 +28,6 @@ struct MobileAISettingsToolbarTests { } } - private func withNavigation( - _ content: Content, - check: (Set) throws -> Void - ) async throws { - try await withHostedView(NavigationStack { content }) { controller in - let navigation = try #require(descendant(of: UINavigationController.self, in: controller)) - try check(Set(toolbarNames(in: navigation.topViewController?.navigationItem))) - } - } - private func withHostedView( _ content: Content, check: (UIViewController) async throws -> Void @@ -112,18 +48,6 @@ struct MobileAISettingsToolbarTests { try await check(controller) } - private func toolbarNames(in item: UINavigationItem?) -> [String] { - guard let item else { return [] } - let buttons = - item.leadingItemGroups.flatMap(\.barButtonItems) - + item.trailingItemGroups.flatMap(\.barButtonItems) - + (item.leftBarButtonItems ?? []) - + (item.rightBarButtonItems ?? []) - return buttons.compactMap { button in - button.accessibilityLabel ?? button.title ?? button.customView?.accessibilityLabel - } - } - private func descendant(of type: T.Type, in controller: UIViewController) -> T? { var pending = [controller] var visited = Set() diff --git a/TFITests/MobileAssistantTranscriptTests.swift b/TFITests/MobileAssistantTranscriptTests.swift index 7407561f..4b3ff105 100644 --- a/TFITests/MobileAssistantTranscriptTests.swift +++ b/TFITests/MobileAssistantTranscriptTests.swift @@ -23,13 +23,6 @@ struct MobileAssistantTranscriptTests { #expect(long <= 320) } - @Test func activityUsesTheAvailableWidthToKeepItsTitleLeading() { - let controller = UIHostingController(rootView: MobileAssistantActivity()) - let size = controller.sizeThatFits(in: CGSize(width: 320, height: 1_000)) - - #expect(size.width == 320) - } - @Test func imageFlowKeepsEveryUserRowTrailingAndAssistantRowsLeading() { let tile = CGSize(width: 104, height: 104) let sizes = Array(repeating: tile, count: 5) diff --git a/TFITests/MobileConnectionJourneyRenderingTests.swift b/TFITests/MobileConnectionJourneyRenderingTests.swift index 6a892890..dc4e8133 100644 --- a/TFITests/MobileConnectionJourneyRenderingTests.swift +++ b/TFITests/MobileConnectionJourneyRenderingTests.swift @@ -9,23 +9,6 @@ import UIKit /// Opt-in native screenshots cover the connection states without opening a transport. @Suite(.serialized) @MainActor struct MobileConnectionJourneyRenderingTests { - @Test func failedJourneyWithoutEndpointsRenders() throws { - let journey = ConnectionJourney() - journey.fail("Network unavailable") - let view = MobileConnectionJourneyView( - journey: journey, library: HostLibraryModel(), authenticationPrompt: nil, hostKey: nil, - reconnectState: nil, submit: { _, _, _, _, _, _ in }, accept: { _ in }, retry: {}, - retryReconnectNow: {}, stopReconnecting: {}, cancel: {} - ) - .frame(width: 390, height: 844) - let renderer = ImageRenderer(content: view) - renderer.scale = 1 - - let image = try #require(renderer.uiImage) - - #expect(image.size == CGSize(width: 390, height: 844)) - } - @Test func renderConnectionJourneyStates() async throws { guard ProcessInfo.processInfo.environment["TERMIND_JOURNEY_SCREENSHOTS"] == "1" else { return } let host = TermindDomain.Host(name: "Production Gateway", host: "gateway.example.invalid") diff --git a/TFITests/MobileConnectionPickerTests.swift b/TFITests/MobileConnectionPickerTests.swift index f7672f92..3383a1dc 100644 --- a/TFITests/MobileConnectionPickerTests.swift +++ b/TFITests/MobileConnectionPickerTests.swift @@ -82,34 +82,7 @@ struct MobileConnectionPickerTests { #expect(Set(rows.map { "\($0.entry.hostID.uuidString):\($0.entry.kind.rawValue)" }).count == rows.count) } - @Test func sharedHostPickerUsesConfiguredTitleWithoutHostsHeaderOrProtocolControl() async throws { - let app = try MobileAppModel(inMemory: true) - app.library.upsert(Host(name: "Production", host: "production.example.invalid")) - - try await withNavigation( - MobileHostPicker( - library: app.library, - title: "Add Server", - emptyDescription: "Add a saved Linux server to start monitoring.", - cancel: {}, - select: { _ in }), - language: "en" - ) { navigation in - #expect(navigation.topViewController?.navigationItem.title == "Add Server") - #expect(descendant(of: UISegmentedControl.self, in: navigation) == nil) - #expect(!labels(in: navigation.view).contains("Hosts")) - } - - try await withNavigation( - MobileConnectionPicker(library: app.library, connect: { _, _ in }, cancel: {}), - language: "en" - ) { navigation in - #expect(descendant(of: UISegmentedControl.self, in: navigation) != nil) - #expect(!labels(in: navigation.view).contains("Hosts")) - } - } - - @Test func snippetHostPickerUsesContextualTitlesAndCheckmarkConfirmation() async throws { + @Test func snippetHostPickerGatesConfirmationOnRequiredSelection() async throws { let app = try MobileAppModel(inMemory: true) let host = Host(name: "Production", host: "production.example.invalid") app.library.upsert(host) @@ -120,7 +93,6 @@ struct MobileConnectionPickerTests { requiresSelection: true, save: { _ in }), language: "en" ) { navigation in - #expect(navigation.topViewController?.navigationItem.title == "Execute on Hosts") let execute = try #require(toolbarButton(named: "Execute", in: navigation)) #expect(!execute.isEnabled) } @@ -131,7 +103,6 @@ struct MobileConnectionPickerTests { requiresSelection: false, save: { _ in }), language: "en" ) { navigation in - #expect(navigation.topViewController?.navigationItem.title == "Add Hosts") let done = try #require(toolbarButton(named: "Done", in: navigation)) #expect(done.isEnabled) } @@ -164,18 +135,6 @@ struct MobileConnectionPickerTests { } } - @Test func workspaceToolbarKeepsMoreAndConnectAvailableWithoutWorkspaces() async throws { - try await withNavigation( - ToolbarHost(hasWorkspaces: false, closeAll: {}, connect: {}), - language: "en" - ) { navigation in - let more = try #require(toolbarButton(named: "More", in: navigation)) - #expect(more.isEnabled) - let connect = try #require(toolbarButton(named: "Connect", in: navigation)) - #expect(connect.isEnabled) - } - } - private func withNavigation( _ content: Content, language: String, @@ -245,24 +204,6 @@ struct MobileConnectionPickerTests { } return nil } - - private func descendant(of type: T.Type, in viewController: UIViewController) -> T? { - descendant(of: type, in: viewController.view) - } - - private func descendant(of type: T.Type, in view: UIView) -> T? { - if let result = view as? T { return result } - for child in view.subviews { - if let result = descendant(of: type, in: child) { return result } - } - return nil - } - - private func labels(in view: UIView) -> [String] { - var result = (view as? UILabel).flatMap(\.text).map { [$0] } ?? [] - for child in view.subviews { result.append(contentsOf: labels(in: child)) } - return result - } } private struct ToolbarHost: View { diff --git a/TFITests/MobileEditorToolbarTests.swift b/TFITests/MobileEditorToolbarTests.swift index 4b810dcc..7284e0f2 100644 --- a/TFITests/MobileEditorToolbarTests.swift +++ b/TFITests/MobileEditorToolbarTests.swift @@ -8,19 +8,11 @@ import UIKit @Suite(.serialized) @MainActor struct MobileEditorToolbarTests { @Test(arguments: ["en", "zh-Hans", "zh-Hant"]) - func iconButtonsKeepTheirNamesAndSaveLifecycle(language: String) async throws { + func saveDisablesActionsUntilCompletionAndThenDismisses(language: String) async throws { let state = EditorState() try await withEditor(state, language: language) { _, navigation in let cancel = try #require(cancelButton(in: navigation)) let save = try #require(saveButton(in: navigation)) - #expect(accessibleName(cancel) == (language == "en" ? "Cancel" : "取消")) - let expectedSave = - switch language { - case "zh-Hans": "保存" - case "zh-Hant": "儲存" - default: "Save" - } - #expect(accessibleName(save) == expectedSave) #expect(cancel.isEnabled) #expect(!save.isEnabled) state.canSave = true @@ -51,8 +43,7 @@ struct MobileEditorToolbarTests { #expect(state.isPresented) #expect(state.title == "Example") #expect(saveButton(in: navigation)?.isEnabled == true) - let alert = try #require(descendant(of: UIAlertController.self, in: controller)) - #expect(alert.title == "Unable to Save") + _ = try #require(descendant(of: UIAlertController.self, in: controller)) } } diff --git a/TFITests/MobileNavigationParityTests.swift b/TFITests/MobileNavigationParityTests.swift index 33699207..fb424795 100644 --- a/TFITests/MobileNavigationParityTests.swift +++ b/TFITests/MobileNavigationParityTests.swift @@ -7,17 +7,6 @@ import Testing @Suite @MainActor struct MobileNavigationParityTests { - @Test func speechAudioTapDoesNotInheritMainActorIsolation() throws { - let root = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - let source = try String( - contentsOf: root.appending(path: "TFI/Platform/IOSSpeechAudioInput.swift"), encoding: .utf8) - - #expect(source.contains("nonisolated static func makeTapHandler")) - #expect(source.contains("block: Self.makeTapHandler")) - } - @Test func vaultReplacesWelcomeAndMetricsRemainsBelowItOnIPad() { let features = MobileFeatureRegistry.standard() #expect(features.registrations(on: .phoneTab).map(\.id) == [.vault, .workspaces, .assistant, .settings]) @@ -27,10 +16,6 @@ struct MobileNavigationParityTests { ) } - @Test func aiSettingsUseThreeNativeManagementPages() { - #expect(MobileAISettingsDestination.allCases == [.models, .skills, .mcp]) - } - @Test func metricsUsesAvailableWidthRatherThanDeviceOrientation() { #expect(!MobileMetricsDetailPage.showsBothColumns(width: 430)) #expect(!MobileMetricsDetailPage.showsBothColumns(width: 680)) diff --git a/TFITests/MobileTerminalListTests.swift b/TFITests/MobileTerminalListTests.swift index ef8d26a3..fb3df5c6 100644 --- a/TFITests/MobileTerminalListTests.swift +++ b/TFITests/MobileTerminalListTests.swift @@ -53,34 +53,6 @@ struct MobileTerminalListTests { } } - @Test func nativeHistoryRemeasuresForAccessibilityDynamicType() async throws { - let app = try MobileAppModel(inMemory: true) - let host = TermindDomain.Host(name: "Test", host: "example.invalid") - app.library.upsert(host) - app.commandHistory.record("echo a command that needs two lines in the compact sheet", for: host.id) - let workspace = MobileWorkspace( - descriptor: MobileWorkspaceDescriptor(hostID: host.id, title: host.name, kind: .ssh), - hosts: app.library, - commandHistory: app.commandHistory) - - try await withHostedView( - MobileCommandHistoryView(workspace: workspace) - .environment(\.dynamicTypeSize, .accessibility3) - ) { root in - let cell = try #require(nativeVisibleCells(in: root.view).first) - #expect(cell.bounds.height > 100) - } - } - - @Test func terminalRiskIndicatorsShareTheCompactPalette() { - #expect(MobileTerminalRiskIndicator.color(for: .safe) == .systemGreen) - #expect(MobileTerminalRiskIndicator.color(for: .caution) == .systemOrange) - #expect(MobileTerminalRiskIndicator.color(for: .destructive) == .systemRed) - #expect(MobileTerminalRiskIndicator.accessibilityIdentifier(for: .safe) == "TerminalRisk.safe") - #expect(MobileTerminalRiskIndicator.accessibilityIdentifier(for: .caution) == "TerminalRisk.caution") - #expect(MobileTerminalRiskIndicator.accessibilityIdentifier(for: .destructive) == "TerminalRisk.destructive") - } - private func withHostedView( _ content: Content, check: (UIViewController) async throws -> Void ) async throws { diff --git a/TFMTests/AgentRuntimeTests.swift b/TFMTests/AgentRuntimeTests.swift index 32ed7f4d..bd6056cc 100644 --- a/TFMTests/AgentRuntimeTests.swift +++ b/TFMTests/AgentRuntimeTests.swift @@ -761,11 +761,10 @@ struct AgentRuntimeTests { event: { event in Task { await events.append(event) } } ) - guard case .deny(let reason) = await broker.authorize(request, mode: .approveForMe) else { + guard case .deny = await broker.authorize(request, mode: .approveForMe) else { Issue.record("Expected timeout denial") return } - #expect(reason.contains("timed out")) try? await Task.sleep(for: .milliseconds(10)) #expect( await events.values.contains { diff --git a/TFMTests/AgentToolPresentationTests.swift b/TFMTests/AgentToolPresentationTests.swift index 849ec74c..0cf62d22 100644 --- a/TFMTests/AgentToolPresentationTests.swift +++ b/TFMTests/AgentToolPresentationTests.swift @@ -191,17 +191,11 @@ struct AgentToolPresentationTests { Issue.record("Expected a grouped host listing") return } - #expect(listing.summary == "3 hosts · 1 connected · 1 temporary") #expect( - listing.sections.map(\.title) == [ - "Production · 1 host", "Production / Edge · 0 hosts", - "Ungrouped · 1 host", "Temporary · 1 host", + listing.sections.map { $0.rows.map(\.title) } == [ + ["API Server"], [], ["Loose Box"], ["Current Terminal"], ]) - #expect(listing.sections[0].rows[0].title == "API Server") - #expect(listing.sections[0].rows[0].subtitle == "deploy@api.example.com · Connected") #expect(listing.sections[0].rows[0].badges == ["prod", "nginx"]) - #expect(listing.sections[2].rows[0].subtitle == "root@10.0.0.2 · Failed") - #expect(listing.sections[3].rows[0].subtitle == "root@10.0.0.8 · Reconnecting") let rendered = String(describing: detail) #expect(!rendered.contains(productionID)) #expect(!rendered.contains("aaaaaaaa-aaaa")) @@ -594,88 +588,6 @@ struct AgentToolPresentationTests { ) } - @Test - func sshExecBatchCountsCommandsAcrossLifecycleStatesAndLocales() { - let presentation = AgentToolDescriptor.Presentation( - symbol: "terminal", - activity: .semanticArgument(key: "command", fallback: .sshCommand), - actionKind: .run - ) - - func cards(_ state: AgentToolCard.State) -> [AgentToolCard] { - ["uname -a", "uptime"].map { command in - var card = makeCard( - name: "ssh_exec", target: .host, - arguments: ["command": .string(command)], presentation: presentation - ) - card.state = state - return card - } - } - - let english = Locale(identifier: "en") - #expect(AgentToolPresentation.batchSummary(for: cards(.running), locale: english) == "Running 2 commands") - #expect(AgentToolPresentation.batchSummary(for: cards(.completed), locale: english) == "Ran 2 commands") - #expect(AgentToolPresentation.batchSummary(for: cards(.failed), locale: english) == "Failed to run 2 commands") - #expect( - AgentToolPresentation.batchSummary( - for: cards(.completed), locale: Locale(identifier: "zh-Hans") - ) == "已运行2条命令" - ) - #expect( - AgentToolPresentation.batchSummary( - for: cards(.completed), locale: Locale(identifier: "zh-Hant") - ) == "已執行2條指令" - ) - } - - @Test - func manageTasksBatchCountsWholeListUpdates() { - let presentation = AgentToolDescriptor.Presentation( - symbol: "checklist", activity: .semanticLabel(.taskList), - actionKind: .update - ) - let cards = (0..<2).map { _ in - var card = makeCard(name: "manage_tasks", target: .user, presentation: presentation) - card.state = .completed - return card - } - - #expect( - AgentToolPresentation.batchSummary(for: cards, locale: Locale(identifier: "en")) - == "Updated the task list 2 times" - ) - #expect( - AgentToolPresentation.batchSummary( - for: cards, locale: Locale(identifier: "zh-Hans") - ) == "已更新任务列表2次" - ) - #expect( - AgentToolPresentation.batchSummary( - for: cards, locale: Locale(identifier: "zh-Hant") - ) == "已更新任務清單2次" - ) - } - - @Test - func unknownSemanticBatchKeepsGenericToolCallFallback() { - let presentation = AgentToolDescriptor.Presentation( - symbol: "wrench.and.screwdriver", - activity: .semanticLabel(.init("database")), - actionKind: .inspect - ) - let cards = (0..<2).map { _ in - var card = makeCard(name: "custom_inspect", target: .mcp, presentation: presentation) - card.state = .completed - return card - } - - #expect( - AgentToolPresentation.batchSummary(for: cards, locale: Locale(identifier: "en")) - == "Inspected 2 tool calls" - ) - } - @Test func reasoningBatchTitleIsPlainSingleLineText() { let title = AgentToolPresentation.reasoningTitle( diff --git a/TFMTests/ArchitectureBoundaryTests.swift b/TFMTests/ArchitectureBoundaryTests.swift index 1fca300a..cab75ebe 100644 --- a/TFMTests/ArchitectureBoundaryTests.swift +++ b/TFMTests/ArchitectureBoundaryTests.swift @@ -1,318 +1,10 @@ import Foundation -import TermindApplication -import TermindDomain -import TermindInfrastructure -import TermindPersistence -import TermindPresentation -import TermindRemoteInfrastructure import Testing @Suite struct ArchitectureBoundaryTests { @Test - func resumableTransferStateRemainsProcessLocal() throws { - let root = sourceRoot.deletingLastPathComponent() - let checkpoint = try String( - contentsOf: root.appending( - path: "Frameworks/TermindRemoteInfrastructure/Transfers/SFTPResumeState.swift"), encoding: .utf8) - for forbidden in ["Codable", "SwiftData", "UserDefaults", "bookmark", "FileHandle", "FileManager"] { - #expect(!checkpoint.contains(forbidden)) - } - let upload = try String( - contentsOf: sourceRoot.appending( - path: "Application/MCPServer/MCPUploadCoordinator.swift"), encoding: .utf8) - #expect(upload.contains("upload-offset")) - #expect(upload.contains("ticket.runID")) - let download = try String( - contentsOf: sourceRoot.appending( - path: "Application/MCPServer/MCPDownloadCoordinator.swift"), encoding: .utf8) - #expect(download.contains("Content-Range")) - #expect(download.contains("ticket.expiresAt > now()")) - } - - @Test - func httpDownloadsUseCheckedRemoteStreamsWithoutLocalFilesOrScratch() throws { - let root = sourceRoot.deletingLastPathComponent() - let coordinator = try String( - contentsOf: sourceRoot.appending(path: "Application/MCPServer/MCPDownloadCoordinator.swift"), - encoding: .utf8) - for forbidden in [ - "AgentScratchWorkspace", "FileHandle", "FileManager", "localURL", "startAccessingSecurityScopedResource", - ] { - #expect(!coordinator.contains(forbidden)) - } - let stream = try String( - contentsOf: root.appending( - path: "Frameworks/TermindRemoteInfrastructure/Transfers/SFTPDownloadStream.swift"), encoding: .utf8) - for forbidden in ["HTTPRequest", "HTTPResponse", "import Network", "AgentScratchWorkspace", "FileHandle"] { - #expect(!stream.contains(forbidden)) - } - let tools = try String( - contentsOf: sourceRoot.appending(path: "Application/MCPServer/MCPServerTools.swift"), encoding: .utf8) - #expect(tools.contains("openDownloadStream(")) - #expect(tools.contains("withDedicatedSFTP(")) - let native = try String( - contentsOf: root.appending(path: "Frameworks/TermindRemoteInfrastructure/SSH/SFTPClient.swift"), - encoding: .utf8) - #expect(native.contains("readDownloadChunk(id:")) - #expect(native.contains("libssh2_sftp_fstat_ex(entry.handle")) - #expect(native.contains("entry.modificationTime")) - } - - @Test - func httpUploadsStayOutsideScratchAndDoNotReadClientFilePaths() throws { - let root = sourceRoot.deletingLastPathComponent() - let coordinator = try String( - contentsOf: sourceRoot.appending(path: "Application/MCPServer/MCPUploadCoordinator.swift"), encoding: .utf8) - for forbidden in [ - "AgentScratchWorkspace", "FileHandle", "FileManager", "localURL", "startAccessingSecurityScopedResource", - ] { - #expect(!coordinator.contains(forbidden)) - } - let stream = try String( - contentsOf: root.appending( - path: "Frameworks/TermindRemoteInfrastructure/Transfers/SFTPStreamUploadOperation.swift"), - encoding: .utf8) - for forbidden in ["HTTPRequest", "HTTPResponse", "import Network", "AgentScratchWorkspace", "FileHandle"] { - #expect(!stream.contains(forbidden)) - } - #expect(stream.contains("digestMismatch")) - #expect(stream.contains("relay.finishWriting()")) - let bridge = try String(contentsOf: root.appending(path: "TermindMCPBridge/main.swift"), encoding: .utf8) - #expect(!bridge.contains("/uploads/")) - } - - @Test - func singleLineEditingIsSharedButNativeFieldEditorsRemainAppOwned() throws { - let root = sourceRoot.deletingLastPathComponent() - let policy = try String( - contentsOf: root.appending(path: "Frameworks/TermindPresentation/SingleLineTextInput.swift"), - encoding: .utf8) - for forbidden in ["import AppKit", "import UIKit", "import TermindPersistence", "import AgentKit"] { - #expect(!policy.contains(forbidden)) - } - let native = try String( - contentsOf: sourceRoot.appending(path: "UI/Components/SingleLineFieldEditing.swift"), encoding: .utf8) - #expect(native.contains("SingleLineTextInput.normalized")) - #expect(native.contains("hasMarkedText()")) - for path in [ - "TFM/Features/AI/ConversationTranscriptView.swift", - "TFI/Features/AI/MobileAssistantComposer.swift", - "TFI/Features/AI/MobileAssistantInteraction.swift", - ] { - let content = try String(contentsOf: root.appending(path: path), encoding: .utf8) - #expect(!content.contains(".singleLineText")) - } - } - - @Test - func aiSettingsUsePortableModelsAndSyncedSkills() throws { - let root = sourceRoot.deletingLastPathComponent() - let library = try String( - contentsOf: root.appending(path: "Frameworks/TermindApplication/FeatureModels/SkillLibraryModel.swift"), - encoding: .utf8) - for forbidden in ["import SwiftData", "import AppKit", "import UIKit", "import TermindPersistence"] { - #expect(!library.contains(forbidden)) - } - let role = try String( - contentsOf: root.appending(path: "Frameworks/TermindPresentation/ModelRoleSettings.swift"), encoding: .utf8) - #expect(role.contains("role.unsetLabel")) - #expect(!role.contains("AppModel")) - let mobile = try String(contentsOf: root.appending(path: "TFI/App/MobileAppModel.swift"), encoding: .utf8) - #expect(mobile.contains("SyncedSkillLibraryPersistence")) - #expect(mobile.contains("skills.applyRemoteSnapshot")) - let settings = try String( - contentsOf: root.appending(path: "TFI/Features/Settings/MobileAISettingsView.swift"), encoding: .utf8) - #expect(!settings.contains("AgentRuntime(")) - } - - @Test - func screenshotScenesShareTheProductionWindowHost() throws { - let source = try String(contentsOf: sourceRoot.appending(path: "App/TermindApp.swift"), encoding: .utf8) - #expect(source.components(separatedBy: "RootView(").count == 2) - #expect(!source.contains("ScreenshotDemoWindow")) - #expect(!source.contains(".suppressed")) - #expect(source.contains(".restorationBehavior(")) - } - - @Test - func reviewInvitationsStayLocalAndOutsideTheAgentRuntime() throws { - let sync = try String( - contentsOf: sourceRoot.appending(path: "../Frameworks/TermindDomain/Settings/SettingsSyncTypes.swift"), - encoding: .utf8 - ) - #expect(!sync.contains("reviewFirstUse")) - #expect(!sync.contains("reviewLastAttempt")) - let controller = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindApplication/FeatureModels/AppReviewController.swift"), encoding: .utf8 - ) - for forbidden in ["import StoreKit", "import AppKit", "import SwiftUI", "import AgentKit"] { - #expect(!controller.contains(forbidden)) - } - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Platform/MobileAppReviewWindowReader.swift"), - encoding: .utf8 - ) - #expect(mobile.contains("ReviewInteractionGestureRecognizer")) - #expect(mobile.contains("controller.removeSurface(surfaceID)")) - } - - @Test - func assistantCompositionIsSharedAndMobileInputRemainsSwiftUI() throws { - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Application/Agent/MobileAssistantModel.swift"), - encoding: .utf8) - let desktop = try String(contentsOf: sourceRoot.appending(path: "App/AppModel.swift"), encoding: .utf8) - for source in [mobile, desktop] { - #expect(source.contains("AssistantExecutionController(")) - #expect(!source.contains("AgentRuntime(")) - } - let composer = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/AI/MobileAssistantComposer.swift"), encoding: .utf8) - #expect(composer.contains("axis: .vertical")) - #expect(!composer.contains("UITextView")) - #expect(!composer.contains("UIViewRepresentable")) - try assertNoImports( - in: "../Frameworks/TermindApplication/Agent", - forbidden: ["TermindInfrastructure", "TermindRemoteInfrastructure", "UIKit", "AppKit", "PDFKit", "Speech"]) - } - - private var sourceRoot: URL { - URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "TFM", directoryHint: .isDirectory) - } - - @Test - func settingsSyncKeepsExecutionHistoryAndLocalPermissionsOutsideItsPayloads() throws { - let records = try String( - contentsOf: sourceRoot.appending(path: "../Frameworks/TermindDomain/Settings/SettingsSyncTypes.swift"), - encoding: .utf8) - for excluded in [ - "AgentPermissionMode", "AgentRun", "AgentConversation", "mcpServerPort", "mcpServerEnabled", - "mcpServerDisabledTools", "mcpserver.token", - ] { - #expect(!records.contains(excluded)) - } - let localServer = try String( - contentsOf: sourceRoot.appending(path: "App/MCPServerController.swift"), encoding: .utf8) - #expect(localServer.contains("MCPServerTokenStore")) - #expect(!localServer.contains("AIConfigurationModel")) - let terminal = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindPersistence/TerminalPreferences+Defaults.swift"), - encoding: .utf8) - #expect(!terminal.contains("dictionaryRepresentation")) - #expect(!terminal.contains("agentPermissionMode")) - } - - @Test - func metricsViewsCannotOwnTransportsAndSecretsAreResolvedOffMainActor() throws { - try assertNoConcreteImplementations(in: "Features/Metrics") - let model = try String( - contentsOf: sourceRoot.appending(path: "../Frameworks/TermindApplication/Metrics/ServerMetricsModel.swift"), - encoding: .utf8) - #expect(!model.contains("hosts.sshConnectionConfiguration(")) - #expect(model.contains("workers[server.id] = Task.detached")) - #expect(model.contains("private nonisolated func run(")) - #expect(model.contains("try await beginRunAttempt(")) - let preparation = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindApplication/Connections/SSHConnectionPreparation.swift"), - encoding: .utf8) - #expect(preparation.contains("Task.detached")) - let view = try String( - contentsOf: sourceRoot.appending(path: "Features/Metrics/MetricsView.swift"), encoding: .utf8) - #expect(!view.contains("SSHExecSession")) - #expect(!view.contains("KeychainSecretStore")) - } - - @Test - func metricsToolDependsOnSnapshotsAndMonitoringDoesNotImportAgentKit() throws { - let tool = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindRemoteInfrastructure/Agent/GetMetricsTool.swift"), encoding: .utf8) - #expect(tool.contains("any MetricsSnapshotReading")) - for forbidden in [ - "SSHMetricsSampler", "AgentConnectionPool", "ServerMetricsModel", "observeDetails(", - "requestAuthentication(", - ] { - #expect(!tool.contains(forbidden)) - } - for path in [ - "../Frameworks/TermindApplication/Metrics/MetricsSnapshot.swift", - "../Frameworks/TermindApplication/Metrics/ServerMetricsModel.swift", - ] { - let source = try String(contentsOf: sourceRoot.appending(path: path), encoding: .utf8) - #expect(!source.contains("import AgentKit")) - } - } - - @Test - func coreDirectoryIsGone() { - let core = sourceRoot.appending(path: "Core", directoryHint: .isDirectory) - #expect(!FileManager.default.fileExists(atPath: core.path)) - } - - @Test - func retiredFeatureDirectoriesAreGone() { - for directory in ["Tabs", "ContextPanel"] { - let url = sourceRoot.appending(path: "Features/\(directory)", directoryHint: .isDirectory) - #expect(!FileManager.default.fileExists(atPath: url.path)) - } - } - - @Test - func appTargetsDoNotReintroduceFrameworkForwardingAliases() throws { - let forwarding = try Regex( - #"(?m)^typealias\s+[A-Za-z_][A-Za-z0-9_]*\s*=\s*(?:Termind|Shared)[A-Za-z0-9_.]*"# - ) - for directory in ["", "../TFI"] { - for file in try swiftFiles(in: directory) { - let source = try String(contentsOf: file, encoding: .utf8) - #expect( - source.firstMatch(of: forwarding) == nil, - "\(file.path) forwards a framework type instead of importing its owner" - ) - } - } - - for file in try swiftFiles(in: "../Frameworks/TermindPersistence") { - #expect(!file.lastPathComponent.hasPrefix("Shared")) - } - - let sharedDeclaration = try Regex( - #"(?m)^public\s+(?:nonisolated\s+)?(?:final\s+)?(?:struct|class|enum|actor|protocol|typealias)\s+Shared[A-Z]"# - ) - for module in [ - "TermindDomain", "TermindApplication", "TermindInfrastructure", - "TermindRemoteInfrastructure", "TermindPresentation", "TermindPersistence", - ] { - for file in try swiftFiles(in: "../Frameworks/\(module)") { - let source = try String(contentsOf: file, encoding: .utf8) - #expect( - source.firstMatch(of: sharedDeclaration) == nil, - "\(file.path) uses Shared as a framework namespace" - ) - } - } - } - - @Test - func mobileTargetUsesTheDesktopLayerVocabulary() throws { - let mobile = sourceRoot.appending(path: "../TFI", directoryHint: .isDirectory) - for name in [ - "App", "Application", "Design", "DesignSystem", "Features", "Infrastructure", "Platform", "UI", - ] { - let directory = mobile.appending(path: name, directoryHint: .isDirectory) - var isDirectory: ObjCBool = false - #expect(FileManager.default.fileExists(atPath: directory.path, isDirectory: &isDirectory)) - #expect(isDirectory.boolValue) - #expect(!(try swiftFiles(in: "../TFI/\(name)")).isEmpty) - } - + func mobileLayersKeepUIAndInfrastructureSeparate() throws { try assertNoImports( in: "../TFI/Application", forbidden: ["SwiftUI", "UIKit", "SwiftData", "TermindPersistence"]) @@ -322,199 +14,6 @@ struct ArchitectureBoundaryTests { try assertNoConcreteImplementations(in: "../TFI/Features") } - @Test - func mobileFeaturesAndLiveServicesEnterThroughRegistries() throws { - let root = try String( - contentsOf: sourceRoot.appending(path: "../TFI/App/MobileRootView.swift"), encoding: .utf8) - let features = try String( - contentsOf: sourceRoot.appending(path: "../TFI/App/MobileFeatureRegistry.swift"), encoding: .utf8) - #expect(root.contains("features.registrations(on: .phoneTab)")) - #expect(root.contains("features.registrations(on: .padSidebar)")) - #expect(!root.contains("switch scene.selectedDestination")) - #expect(features.contains("mutating func register(")) - #expect(features.contains("Duplicate mobile feature registration")) - #expect(features.contains("model: scene.assistant")) - #expect(!features.contains("model: app.assistant")) - - let scene = try String( - contentsOf: sourceRoot.appending(path: "../TFI/App/MobileSceneModel.swift"), encoding: .utf8) - #expect(scene.contains("let assistant: MobileAssistantSceneModel")) - - let descriptor = try String( - contentsOf: sourceRoot.appending( - path: "../TFI/Application/Workspaces/MobileWorkspaceDescriptor.swift"), encoding: .utf8) - #expect(descriptor.contains("var kind: ConnectionHistoryEntry.Kind")) - #expect(!descriptor.contains("enum Kind")) - - let assistant = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Application/Agent/MobileAssistantModel.swift"), - encoding: .utf8) - let metrics = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Application/Metrics/MobileMetricsModel.swift"), - encoding: .utf8) - for concrete in ["SSHExecSession()", "SFTPClient()"] { - #expect(!assistant.contains(concrete)) - } - for concrete in ["SSHMetricsSampler", "SSHExecSession"] { - #expect(!metrics.contains(concrete)) - } - } - - @Test - func sessionFeaturesDependOnTheConnectionPort() throws { - for path in ["Features/Terminal/SSHSessionModel.swift", "Features/SFTP/SFTPSessionModel.swift"] { - let text = try String( - contentsOf: sourceRoot.appending(path: path), encoding: .utf8 - ) - #expect(!text.contains("HostLibraryModel"), "\(path) bypasses RemoteSessionHostStore") - } - } - - @Test - func sshHostKeyTrustStaysInsideTheLiveConnectionAttempt() throws { - let request = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindApplication/SSHConnectionTypes.swift" - ), - encoding: .utf8 - ) - #expect(request.contains("struct SSHConnectionRequest")) - #expect(request.contains("SSHHostKeyAuthorizer")) - - for root in [ - "../Frameworks/TermindRemoteInfrastructure", "Features/Terminal", - "Features/SFTP", "../TFI", - ] { - for file in try swiftFiles(in: root) { - let source = try String(contentsOf: file, encoding: .utf8) - #expect(!source.contains("discoverHostKey"), "\(file.path) opens a separate host-key probe") - #expect(!source.contains("probe complete"), "\(file.path) closes an unauthenticated probe") - } - } - } - - @Test - func remoteDirectoryStateDependsOnTheApplicationPort() throws { - let value = try String( - contentsOf: sourceRoot.appending(path: "../Frameworks/TermindDomain/Transfers/RemoteFile.swift"), - encoding: .utf8 - ) - #expect(value.contains("struct RemoteFile")) - #expect(!value.contains("symbol:")) - #expect(!value.contains("sftpKindDescription")) - - let transport = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindRemoteInfrastructure/SSH/SFTPClient.swift"), - encoding: .utf8 - ) - #expect(transport.contains("SFTPClientProtocol: RemoteFileSystem")) - #expect(!transport.contains("struct RemoteFile")) - #expect(transport.contains("sourceSucceeded()")) - #expect(transport.contains("observeCancellation { relay.stop() }")) - - let copy = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindRemoteInfrastructure/Agent/SFTPCopyTool.swift"), - encoding: .utf8 - ) - #expect(copy.contains("withDedicatedSFTP")) - #expect(!copy.contains("AgentScratchWorkspace")) - #expect(!copy.contains("SFTPClient()")) - - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Application/Transfers/MobileSFTPSessionModel.swift"), - encoding: .utf8 - ) - #expect(mobile.contains("any RemoteFileSystem")) - #expect(!mobile.contains("TermindRemoteInfrastructure")) - - let workspace = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/Workspaces/MobileWorkspace.swift"), - encoding: .utf8 - ) - #expect(!workspace.contains("private(set) var files: [RemoteFile]")) - #expect(!workspace.contains("var path = \"/\"")) - } - - @Test - func terminalPresentationPolicyIsSharedAcrossApps() throws { - let presentation = try String( - contentsOf: sourceRoot.appending( - path: "../Frameworks/TermindPresentation/Terminal/TerminalAppearance.swift"), - encoding: .utf8 - ) - #expect(presentation.contains("enum TermindTerminalThemeCatalog")) - #expect(presentation.contains("func applyPreferences")) - #expect( - !FileManager.default.fileExists( - atPath: sourceRoot.appending(path: "../TFI/Platform/MobileTerminalAppearance.swift").path - ) - ) - - let desktopTheme = try String( - contentsOf: sourceRoot.appending(path: "Design/TerminalTheme.swift"), encoding: .utf8) - #expect(!desktopTheme.contains("enum TermindTerminalThemeCatalog")) - #expect(!desktopTheme.contains("func applyAppearance")) - - for path in [ - "Features/Terminal/SSHSessionModel.swift", - "Features/Terminal/SerialSessionModel.swift", - "../TFI/Features/Workspaces/MobileWorkspace.swift", - ] { - let source = try String(contentsOf: sourceRoot.appending(path: path), encoding: .utf8) - #expect(source.contains("TerminalFindController("), "\(path) does not use shared Find state") - #expect(!source.contains("var findQuery ="), "\(path) owns a second Find query") - } - - for path in [ - "UI/Components/ConnectionJourneyView.swift", - "../TFI/UI/Components/MobileConnectionJourneyView.swift", - ] { - let source = try String(contentsOf: sourceRoot.appending(path: path), encoding: .utf8) - #expect(source.contains("ConnectionJourneyHeader(")) - #expect(source.contains("ConnectionJourneyReconnectStatus(")) - #expect(!source.contains("private var headerSymbol")) - } - } - - @Test - func retiredContextPanelTypesAreGone() throws { - for file in try swiftFiles(in: "Features") { - let text = try String(contentsOf: file, encoding: .utf8) - #expect(!text.contains("ContextPanel"), "\(file.path) retains retired ContextPanel naming") - } - } - - @Test - func customKeyboardShortcutsUseTheApplicationRegistry() throws { - for file in try swiftFiles(in: "") - where file.lastPathComponent != "AppShortcut.swift" { - let lines = try String(contentsOf: file, encoding: .utf8) - .split(separator: "\n", omittingEmptySubsequences: false) - for (offset, line) in lines.enumerated() where line.contains(".keyboardShortcut(") { - let isStandardDialogAction = - line.contains(".defaultAction") - || line.contains(".cancelAction") - #expect( - isStandardDialogAction, - "\(file.path):\(offset + 1) bypasses AppShortcut" - ) - } - } - } - - @Test - func featureFacadesDoNotExposeTheirBackingLibrary() throws { - for file in try swiftFiles(in: "../Frameworks/TermindApplication/FeatureModels") { - let text = try String(contentsOf: file, encoding: .utf8) - #expect( - !text.contains("\n let library: HostLibraryModel"), - "\(file.path) exposes the backing aggregate" - ) - } - } - @Test func domainImportsOnlyPlatformIndependentFrameworks() throws { try assertNoImports( @@ -574,23 +73,87 @@ struct ArchitectureBoundaryTests { } ) - #expect(importers == allowed) + #expect(importers.isSubset(of: allowed)) + } - let mobileConsumers = try Set( - swiftFiles(in: "../TFI").compactMap { file -> String? in - let text = try String(contentsOf: file, encoding: .utf8) - guard text.contains("MobileNativeList(") else { return nil } - return file.standardizedFileURL.path.replacingOccurrences( - of: repositoryRoot.path + "/", with: "") - } + /// App-local types shadow imports, so redefining these would silently fork AgentKit. + @Test + func typesOwnedByTheAgentPackageAreNotRedefinedHere() throws { + let owned = [ + "AgentRuntime", "AgentToolExecutor", "AgentToolScheduler", "AgentTurnDriver", + "AgentToolCatalog", "AgentToolRegistry", "AgentEventChannel", "AgentContextPipeline", + "AgentSecretLifecycle", "AgentApprovalBroker", "SecretBroker", "AgentProviderClient", + "AgentModelRetryPolicy", "AgentModelRetryProgress", + "MCPClient", "MCPServer", "ModelProvider", "AIModel", "AgentSkill", + ] + let declaration = try Regex( + #"(?m)^\s*(?:public |internal |private |fileprivate |nonisolated |final |indirect )*"# + + #"(?:struct|class|enum|actor|protocol|typealias)\s+([A-Za-z_][A-Za-z0-9_]*)"# ) - #expect( - mobileConsumers == [ - "TFI/Features/AI/MobileAssistantConversation.swift", - "TFI/Features/SFTP/MobileSFTPBrowser.swift", + for file in try swiftFiles(in: "") { + let text = try String(contentsOf: file, encoding: .utf8) + for match in text.matches(of: declaration) { + let name = String(match.output[1].substring ?? "") + #expect( + !owned.contains(name), + "\(file.path) redefines \(name), which AgentKit owns" + ) + } + } + } + + @Test + func sharedFrameworksCannotImportPlatformAppsOrUI() throws { + for module in [ + "TermindDomain", "TermindApplication", "TermindInfrastructure", "TermindRemoteInfrastructure", + "TermindPersistence", + ] { + try assertNoImports( + in: "../Frameworks/\(module)", + forbidden: ["Termind", "TermindIOS", "AppKit", "UIKit", "IOKit", "ServiceManagement"]) + } + try assertNoImports( + in: "../Frameworks/TermindApplication", + forbidden: ["TermindInfrastructure", "TermindPersistence", "SwiftData"]) + try assertNoImports( + in: "../Frameworks/TermindPersistence", + forbidden: ["TermindInfrastructure", "TermindRemoteInfrastructure"]) + } + + @Test + func sharedPresentationHasNoPlatformOrStorageDependency() throws { + try assertNoImports( + in: "../Frameworks/TermindPresentation", + forbidden: [ + "Termind", "TermindIOS", "UIKit", "AppKit", "SwiftData", "TermindPersistence", + "TermindRemoteInfrastructure", ]) } + @Test + func swiftDataModelsHaveOneSharedOwner() throws { + for directory in [ + "", "../TFI", "../Frameworks/TermindDomain", "../Frameworks/TermindApplication", + "../Frameworks/TermindInfrastructure", + ] { + for file in try swiftFiles(in: directory) { + let source = try String(contentsOf: file, encoding: .utf8) + #expect( + source.range(of: #"(?m)^\s*@Model\b"#, options: .regularExpression) == nil, + "\(file.path) declares a model outside TermindPersistence") + } + } + let models = try swiftFiles(in: "../Frameworks/TermindPersistence") + #expect(!models.isEmpty) + } + + private var sourceRoot: URL { + URL(fileURLWithPath: #filePath) + .deletingLastPathComponent() + .deletingLastPathComponent() + .appending(path: "TFM", directoryHint: .isDirectory) + } + private func assertNoImports(in directory: String, forbidden: Set) throws { for file in try swiftFiles(in: directory) { let text = try String(contentsOf: file, encoding: .utf8) @@ -632,188 +195,6 @@ struct ArchitectureBoundaryTests { } } - /// The agent runtime lives in AgentKit and must keep living there. - /// - /// A redefinition here would compile: Swift resolves the app's own type - /// ahead of an imported one, so the app would silently fork the runtime and - /// nothing would fail until a card drawn by one was replayed by the other. - /// Checked by definition rather than by import, because the app is expected - /// to import AgentKit everywhere. - @Test - func typesOwnedByTheAgentPackageAreNotRedefinedHere() throws { - let owned = [ - "AgentRuntime", "AgentToolExecutor", "AgentToolScheduler", "AgentTurnDriver", - "AgentToolCatalog", "AgentToolRegistry", "AgentEventChannel", "AgentContextPipeline", - "AgentSecretLifecycle", "AgentApprovalBroker", "SecretBroker", "AgentProviderClient", - "AgentModelRetryPolicy", "AgentModelRetryProgress", - "MCPClient", "MCPServer", "ModelProvider", "AIModel", "AgentSkill", - ] - let declaration = try Regex( - #"(?m)^\s*(?:public |internal |private |fileprivate |nonisolated |final |indirect )*"# - + #"(?:struct|class|enum|actor|protocol|typealias)\s+([A-Za-z_][A-Za-z0-9_]*)"# - ) - for file in try swiftFiles(in: "") { - let text = try String(contentsOf: file, encoding: .utf8) - for match in text.matches(of: declaration) { - let name = String(match.output[1].substring ?? "") - #expect( - !owned.contains(name), - "\(file.path) redefines \(name), which AgentKit owns" - ) - } - } - } - - @Test - func mobilePortUsesSharedStoresAndSeparateSigningFiles() throws { - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/App/MobileAppModel.swift"), encoding: .utf8) - #expect(mobile.contains("SyncedAIConfigurationPersistence(settingsPersistence)")) - #expect(mobile.contains("settingsSync?.didApplyRemote")) - #expect(mobile.contains("vaultSync?.didApplyRemote")) - let project = try String( - contentsOf: sourceRoot.appending(path: "../Termind.xcodeproj/project.pbxproj"), encoding: .utf8) - #expect(project.contains("TFI/TFI.entitlements")) - #expect(project.contains("TFM/TFM.entitlements")) - #expect(!project.contains("Termind.entitlements")) - let terminal = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/Settings/MobileTerminalSettingsView.swift"), - encoding: .utf8) - #expect(!terminal.contains("remoteWindowTitleInMenuBar")) - let cloud = try swiftFiles(in: "../Frameworks/TermindInfrastructure/Cloud") - #expect(cloud.count == 3) - } - - @Test - func forwardingEditorsShareTheFlatFormAndContextualLabels() throws { - let desktop = try String( - contentsOf: sourceRoot.appending(path: "Features/Ports/PortForwardingView.swift"), encoding: .utf8) - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/Ports/MobileForwardingEditor.swift"), - encoding: .utf8) - let mobileHost = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/HostEditor/MobileHostEditor.swift"), - encoding: .utf8) - let shared = try String( - contentsOf: sourceRoot.appending(path: "../Frameworks/TermindPresentation/PortForwardingFields.swift"), - encoding: .utf8) - - #expect(shared.contains("Section(\"Port Forwarding\")")) - for editor in [desktop, mobile] { - #expect(editor.contains("PortForwardingFields(rule: $draft)")) - #expect(editor.contains("New Forwarding")) - #expect(editor.contains("Edit Forwarding")) - } - #expect(desktop.contains("FormNavigationAction(title: \"Add Forwarding\")")) - #expect(desktop.contains("Button(\"Enable All\"")) - #expect(desktop.contains("Button(\"Disable All\"")) - #expect(desktop.contains("Waiting for Save")) - #expect(mobileHost.contains("Button(\"Add Forwarding\", systemImage: \"plus\")")) - #expect(mobileHost.contains("Waiting for Save")) - #expect(mobileHost.contains("isNew: !draftForwardings.contains")) - for removed in ["Add Port Forward", "New Port Forward", "Edit Port Forward"] { - #expect(!desktop.contains(removed)) - #expect(!mobile.contains(removed)) - #expect(!mobileHost.contains(removed)) - } - for removed in ["DisclosureGroup(\"Advanced\"", "Section(\"SSH Host\")", "Section(\"Route Preview\")"] { - #expect(!desktop.contains(removed)) - } - } - - @Test - func sharedFrameworksCannotImportPlatformAppsOrUI() throws { - for module in [ - "TermindDomain", "TermindApplication", "TermindInfrastructure", "TermindRemoteInfrastructure", - "TermindPersistence", - ] { - try assertNoImports( - in: "../Frameworks/\(module)", - forbidden: ["Termind", "TermindIOS", "AppKit", "UIKit", "IOKit", "ServiceManagement"]) - } - try assertNoImports( - in: "../Frameworks/TermindApplication", - forbidden: ["TermindInfrastructure", "TermindPersistence", "SwiftData"]) - try assertNoImports( - in: "../Frameworks/TermindPersistence", - forbidden: ["TermindInfrastructure", "TermindRemoteInfrastructure"]) - } - - @Test - func sharedPresentationHasNoPlatformOrStorageDependency() throws { - try assertNoImports( - in: "../Frameworks/TermindPresentation", - forbidden: [ - "Termind", "TermindIOS", "UIKit", "AppKit", "SwiftData", "TermindPersistence", - "TermindRemoteInfrastructure", - ]) - } - - @Test - func fullSurfaceDropOverlayHasOneSharedPresentationOwner() throws { - let sharedPath = sourceRoot.appending( - path: "../Frameworks/TermindPresentation/DropTargetOverlay.swift") - let shared = try String(contentsOf: sharedPath, encoding: .utf8) - - #expect(shared.contains("public struct DropTargetOverlay")) - #expect(shared.contains("public nonisolated struct DropTargetPresentation")) - #expect( - !FileManager.default.fileExists( - atPath: sourceRoot.appending(path: "UI/Components/DropTargetOverlay.swift").path - ) - ) - - for path in [ - "Features/AI/ConversationTranscriptView.swift", - "../TFI/Features/AI/MobileAssistantConversation.swift", - "../TFI/Features/Workspaces/MobileWorkspaceView.swift", - ] { - let consumer = try String( - contentsOf: sourceRoot.appending(path: path), encoding: .utf8) - #expect(consumer.contains("DropTargetOverlay("), "\(path) does not reuse the shared overlay") - } - } - - @Test - func sftpUploadTargetsTheCurrentOpenDirectoryAcrossPlatforms() throws { - let desktop = try String( - contentsOf: sourceRoot.appending(path: "Features/SFTP/SFTPSessionModel.swift"), - encoding: .utf8 - ) - let mobile = try String( - contentsOf: sourceRoot.appending(path: "../TFI/Features/Workspaces/MobileWorkspace.swift"), - encoding: .utf8 - ) - let desktopUpload = try #require( - desktop.components(separatedBy: "func prepareUpload").last? - .components(separatedBy: "func prepareDownload").first - ) - let mobileDestination = try #require( - mobile.components(separatedBy: "var uploadDestination").last? - .components(separatedBy: "var uploadTargetProblem").first - ) - - #expect(desktopUpload.contains("destination: path")) - #expect(mobileDestination.contains("return sftpSession.path")) - } - - @Test - func swiftDataModelsHaveOneSharedOwner() throws { - for directory in [ - "", "../TFI", "../Frameworks/TermindDomain", "../Frameworks/TermindApplication", - "../Frameworks/TermindInfrastructure", - ] { - for file in try swiftFiles(in: directory) { - let source = try String(contentsOf: file, encoding: .utf8) - #expect( - source.range(of: #"(?m)^\s*@Model\b"#, options: .regularExpression) == nil, - "\(file.path) declares a model outside TermindPersistence") - } - } - let models = try swiftFiles(in: "../Frameworks/TermindPersistence") - #expect(!models.isEmpty) - } - private func swiftFiles(in directory: String) throws -> [URL] { let root = sourceRoot.appending(path: directory, directoryHint: .isDirectory) let keys: [URLResourceKey] = [.isRegularFileKey] diff --git a/TFMTests/ColorSystemTests.swift b/TFMTests/ColorSystemTests.swift index 4cc7f641..db61a268 100644 --- a/TFMTests/ColorSystemTests.swift +++ b/TFMTests/ColorSystemTests.swift @@ -68,74 +68,6 @@ struct ColorSystemTests { #expect(colorDistance(light, dark) > 0.5) } - @Test - func featureUIHasNoLiteralDisplayColorsOrDecorativeBlue() throws { - let sourceRoot = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "TFM", directoryHint: .isDirectory) - let exemptPaths = ["DesignSystem/TagColor+Presentation.swift"] - let forbidden = [ - "Color.white", "Color.black", "NSColor.white", "NSColor.gray", - "foregroundStyle(.white)", "AnyShapeStyle(.white)", - ".shadow(color: .black", "Color(white:", "Color(red:", "NSColor(red:", - ".blue", "systemBlue", - ] - - for file in try swiftFiles(in: sourceRoot) { - let relative = file.path.replacingOccurrences( - of: sourceRoot.path + "/", - with: "" - ) - if exemptPaths.contains(relative) { continue } - let lines = try String(contentsOf: file, encoding: .utf8) - .split(separator: "\n", omittingEmptySubsequences: false) - for (offset, line) in lines.enumerated() { - // Black and white are alpha values here, not visible UI hues. - let isMask = - relative == "Features/AI/TranscriptNativeRows.swift" - && line.contains("mask.colors") - if isMask { continue } - // A `TagColor` case a user picked for a host or key, not a hue - // this code chose to draw. Its presentation is resolved through - // the exempt `TagColor+Presentation.swift`, which is the file - // this scan exists to funnel such choices into. - if line.contains("colorTag:") { continue } - for token in forbidden { - #expect( - !line.contains(token), - "\(relative):\(offset + 1) bypasses TermindColor with \(token)" - ) - } - } - } - } - - @Test - func nativeHostingListsReloadRowsWhenTheirEnvironmentChanges() throws { - let sourceRoot = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "TFM", directoryHint: .isDirectory) - - for file in try swiftFiles(in: sourceRoot) { - let text = try String(contentsOf: file, encoding: .utf8) - let environmentChangeCount = - text.components( - separatedBy: "let environmentChanged" - ).count - 1 - guard environmentChangeCount > 0 else { continue } - let forcedReloadCount = - text.components( - separatedBy: "if environmentChanged { listView.reloadData() }" - ).count - 1 - #expect( - forcedReloadCount == environmentChangeCount, - "\(file.path) can leave recycled NSHostingView rows in the old appearance" - ) - } - } - private func resolved( _ color: Color, appearance name: NSAppearance.Name @@ -171,19 +103,4 @@ struct ColorSystemTests { + 0.7152 * linear(color.greenComponent) + 0.0722 * linear(color.blueComponent) } - - private func swiftFiles(in root: URL) throws -> [URL] { - let keys: Set = [.isRegularFileKey] - guard - let enumerator = FileManager.default.enumerator( - at: root, - includingPropertiesForKeys: Array(keys) - ) - else { return [] } - return try enumerator.compactMap { value in - guard let url = value as? URL, url.pathExtension == "swift" else { return nil } - let values = try url.resourceValues(forKeys: keys) - return values.isRegularFile == true ? url : nil - } - } } diff --git a/TFMTests/CommerceConfigurationTests.swift b/TFMTests/CommerceConfigurationTests.swift index 7477fe3c..2300620d 100644 --- a/TFMTests/CommerceConfigurationTests.swift +++ b/TFMTests/CommerceConfigurationTests.swift @@ -10,29 +10,6 @@ import Testing @testable import Termind struct CommerceConfigurationTests { - @Test - func normalDebugLaunchUsesLiveCommerce() throws { - let appFile = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "TFM/App/TermindApp.swift") - let app = try String(contentsOf: appFile, encoding: .utf8) - - #expect(!app.contains(".developmentDefault()")) - #expect(app.contains(": .live()")) - } - - @Test - func appTargetDeclaresInAppPurchaseCapability() throws { - let projectFile = URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "Termind.xcodeproj/project.pbxproj") - let project = try String(contentsOf: projectFile, encoding: .utf8) - - #expect(project.contains("com.apple.InAppPurchase = {")) - } - @Test func localStoreKitProductMatchesPurchaseRequest() throws { let configurationFile = URL(fileURLWithPath: #filePath) diff --git a/TFMTests/ConnectionJourneyTests.swift b/TFMTests/ConnectionJourneyTests.swift index e84c1ed8..2043d950 100644 --- a/TFMTests/ConnectionJourneyTests.swift +++ b/TFMTests/ConnectionJourneyTests.swift @@ -48,7 +48,6 @@ struct ConnectionJourneyTests { #expect(journey.mode == .reconnection) #expect(journey.phase == .connecting) #expect(journey.isPresented) - #expect(journey.plainTextLog.contains("Reconnecting SSH session")) } @Test @@ -114,7 +113,6 @@ struct ConnectionJourneyTests { journey.waitForLocalPassphrase(at: destination.id) #expect(journey.waitsForLocalPassphrase) #expect(!journey.waitsForLocalIdentity) - #expect(journey.plainTextLog.contains("Waiting for a Passphrase")) journey.waitForLocalIdentity(at: destination.id) #expect(journey.waitsForLocalIdentity) diff --git a/TFMTests/KeychainOrderTests.swift b/TFMTests/KeychainOrderTests.swift index 18a2fe0a..bb9f3666 100644 --- a/TFMTests/KeychainOrderTests.swift +++ b/TFMTests/KeychainOrderTests.swift @@ -13,33 +13,6 @@ import Testing @Suite @MainActor struct KeychainOrderTests { - private func storagePrecedesColor(in source: String, occurrences: Int) -> Bool { - var searchRange = source.startIndex.. normal.height) } diff --git a/TFMTests/MenuSymbolTests.swift b/TFMTests/MenuSymbolTests.swift index 8ce0c122..a64ed533 100644 --- a/TFMTests/MenuSymbolTests.swift +++ b/TFMTests/MenuSymbolTests.swift @@ -12,13 +12,6 @@ import Testing @Suite struct MenuSymbolTests { - private var vocabularySource: URL { - URL(fileURLWithPath: #filePath) - .deletingLastPathComponent() - .deletingLastPathComponent() - .appending(path: "TFM/DesignSystem/MenuSymbol.swift") - } - /// A misspelled SF Symbol draws nothing and reports nothing, so the menu /// simply loses one glyph and no one finds out. This is the only place that /// notices. @@ -32,26 +25,6 @@ struct MenuSymbolTests { } } - /// `all` is hand-written, so a new verb can be added and left out of it — - /// which would quietly exempt the new glyph from the check above. - @Test - func theCheckedListCoversEveryDeclaredGlyph() throws { - let text = try String(contentsOf: vocabularySource, encoding: .utf8) - let declarations = text.split(separator: "\n").compactMap { line -> String? in - let trimmed = line.trimmingCharacters(in: .whitespaces) - guard trimmed.hasPrefix("static let "), trimmed.contains("= \"") else { return nil } - let name = trimmed.dropFirst("static let ".count).prefix { $0 != " " } - return String(name) - } - #expect(!declarations.isEmpty) - - let listMarker = "static let all: [String] = [" - let listed = text.components(separatedBy: listMarker).last ?? "" - for name in declarations { - #expect(listed.contains(name), "MenuSymbol.\(name) is missing from `all`") - } - } - @Test @MainActor func menuBarBrandAssetLoadsAsATransparentTemplate() throws { diff --git a/TFMTests/MetricsInteractionTests.swift b/TFMTests/MetricsInteractionTests.swift index 5d7ce592..7dd6e881 100644 --- a/TFMTests/MetricsInteractionTests.swift +++ b/TFMTests/MetricsInteractionTests.swift @@ -34,18 +34,6 @@ struct MetricsInteractionTests { #expect(window.isToolbarFindFocused) } - @Test - func processColumnsSupportAscendingAndDescendingSort() { - let rows = [ - MetricsProcess(pid: 20, started: 1, name: "zeta", cpu: 30, memory: 200), - MetricsProcess(pid: 2, started: 1, name: "alpha", cpu: 70, memory: 100), - ] - #expect(rows.sorted(using: KeyPathComparator(\.pid)).map(\.pid) == [2, 20]) - #expect(rows.sorted(using: KeyPathComparator(\.name)).map(\.pid) == [2, 20]) - #expect(rows.sorted(using: KeyPathComparator(\.cpuSortValue, order: .reverse)).map(\.pid) == [2, 20]) - #expect(rows.sorted(using: KeyPathComparator(\.memorySortValue, order: .reverse)).map(\.pid) == [20, 2]) - } - @Test(arguments: [HostChooserPurpose.connect, .batchStartup, .metrics]) func hostChoosersRenderWithTheirRequiredEnvironment( purpose: HostChooserPurpose diff --git a/TFMTests/MetricsPresentationTests.swift b/TFMTests/MetricsPresentationTests.swift index 68c45694..31543b98 100644 --- a/TFMTests/MetricsPresentationTests.swift +++ b/TFMTests/MetricsPresentationTests.swift @@ -39,69 +39,6 @@ struct MetricsPresentationTests { #expect(try height() == connecting) } - @Test - func diskColumnsRenderAcrossTheFullAvailableWidth() throws { - let io = MetricsDevicePoint( - id: "8:1", read: 0, write: 0, readTotal: 438.4 * 1_073_741_824, - writeTotal: 625.2 * 1_073_741_824, readIOPS: 0, writeIOPS: 0 - ) - for width in [246.0, 300.0] { - let renderer = ImageRenderer( - content: MetricsDiskTable(io: io).frame(width: width) - .environment(\.locale, Locale(identifier: "zh-Hans"))) - renderer.scale = 2 - let image = try #require(renderer.nsImage) - #expect(image.size.width == CGFloat(width)) - #expect(image.size.height == 52) - let bitmap = try #require(image.tiffRepresentation.flatMap { NSBitmapImageRep(data: $0) }) - let png = try #require(bitmap.representation(using: .png, properties: [:])) - try png.write( - to: FileManager.default.temporaryDirectory.appending(path: "termind-disk-table-\(Int(width)).png")) - } - } - - @Test - func nvidiaDetailsRenderWithTwoSimulatedDevicesWithoutConnecting() throws { - let gib = 1_073_741_824.0 - let server = ServerMetric(selection: MetricsSelection(id: UUID()), name: "GPU Preview") - server.isConnecting = false - server.gpuState = .available - server.gpuSnapshot = NvidiaSnapshot( - cudaVersion: "12.4", - devices: [ - NvidiaGPU( - id: "GPU-00000000-0000-0000-0000-000000000001", name: "NVIDIA GPU 0", - utilization: 74, memoryUsed: 17.3 * gib, memoryTotal: 24 * gib, memoryFree: 6.7 * gib, - memoryUtilization: 51, encoder: 12, decoder: 0, fan: 45, vbios: "95.02.3C.00.01" - ), - NvidiaGPU( - id: "GPU-00000000-0000-0000-0000-000000000002", name: "NVIDIA GPU 1", - utilization: 18, memoryUsed: 4.1 * gib, memoryTotal: 24 * gib, memoryFree: 19.9 * gib, - memoryUtilization: 9, encoder: 0, decoder: 0, fan: nil, vbios: "95.02.3C.00.01" - ), - ]) - for language in ["en", "zh-Hans", "zh-Hant"] { - for expanded in [false, true] { - let content = MetricsGPUSection(server: server, initiallyExpanded: expanded) - .frame(width: 324, alignment: .leading) - .padding(12).background(TermindColor.windowSurface) - .environment(\.locale, Locale(identifier: language)) - .environment(\.colorScheme, .light) - let renderer = ImageRenderer(content: content) - renderer.scale = 2 - var image: NSImage? - NSAppearance(named: .aqua)?.performAsCurrentDrawingAppearance { image = renderer.nsImage } - let bitmap = try #require(image?.tiffRepresentation.flatMap { NSBitmapImageRep(data: $0) }) - let png = try #require(bitmap.representation(using: .png, properties: [:])) - #expect(bitmap.pixelsWide == 696) - #expect(bitmap.pixelsHigh > 600) - try png.write( - to: FileManager.default.temporaryDirectory.appending( - path: "termind-gpu-preview-\(language)\(expanded ? "-expanded" : "").png")) - } - } - } - @Test func swapRendersAsASeparatePanelBelowMemoryAndHidesAtZeroCapacity() throws { let withSwap = ServerMetric(selection: MetricsSelection(id: UUID()), name: "Swap Preview") @@ -138,46 +75,6 @@ struct MetricsPresentationTests { } } } - - @Test - func compactCardsRenderInEveryLanguageAndAppearance() throws { - let hosts = HostLibraryModel() - let model = ServerMetricsModel( - hosts: hosts, preferences: PreviewMetricsPreferences(), - makeSampler: { SSHMetricsSampler(session: RemoteConnectionCoordinator().makeExecSession()) } - ) - let server = ServerMetric(selection: MetricsSelection(id: UUID()), name: "ElasticSearch") - server.isConnecting = false - server.status = "" - server.point = MetricsPoint( - date: Date(), cpu: 94, memory: 65, memoryUsed: 2_400_000_000, memoryTotal: 4_000_000_000, - diskRead: 89_000_000, diskWrite: 161_000, networkReceive: 32_000, networkSend: 18_000 - ) - server.gpuState = .unavailable - for language in ["en", "zh-Hans", "zh-Hant"] { - for dark in [false, true] { - let content = ServerMetricsCard( - server: server, model: model, compact: true, inspect: {}, openTerminal: {} - ) - .frame(width: 350).padding(14) - .background(TermindColor.windowSurface) - .environment(\.locale, Locale(identifier: language)) - .environment(\.colorScheme, dark ? .dark : .light) - let renderer = ImageRenderer(content: content) - renderer.scale = 2 - var image: NSImage? - NSAppearance(named: dark ? .darkAqua : .aqua)?.performAsCurrentDrawingAppearance { - image = renderer.nsImage - } - let bitmap = try #require(image?.tiffRepresentation.flatMap { NSBitmapImageRep(data: $0) }) - let png = try #require(bitmap.representation(using: .png, properties: [:])) - #expect(bitmap.pixelsWide >= 700) - try png.write( - to: FileManager.default.temporaryDirectory.appending( - path: "termind-metrics-\(language)-\(dark ? "dark" : "light").png")) - } - } - } } private final class PreviewMetricsPreferences: MetricsConfigurationStoring { diff --git a/TFMTests/OnboardingControllerTests.swift b/TFMTests/OnboardingControllerTests.swift index 121e2e27..aba49d4b 100644 --- a/TFMTests/OnboardingControllerTests.swift +++ b/TFMTests/OnboardingControllerTests.swift @@ -98,15 +98,13 @@ struct OnboardingControllerTests { controller.preview(.commandGenerator) controller.consider(window: window, context: OnboardingContext()) guard - case .waitingForContext(let reason) = controller.developerStatus( + case .waitingForContext = controller.developerStatus( for: .commandGenerator ) else { Issue.record("Expected Preview to wait for a shell context") return } - #expect(reason.contains("supported SSH shell")) - controller.consider( window: window, context: OnboardingContext(canGenerateCommand: true) diff --git a/TFMTests/SSHImportTests.swift b/TFMTests/SSHImportTests.swift index a72cd127..12fdf034 100644 --- a/TFMTests/SSHImportTests.swift +++ b/TFMTests/SSHImportTests.swift @@ -171,30 +171,6 @@ struct SSHImportTests { } } - @Test - func importPreviewKeepsItemsAsDirectFormRows() throws { - let sourceRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent().deletingLastPathComponent() - let source = try String( - contentsOf: sourceRoot.appendingPathComponent("TFM/Features/SSHImport/SSHImportView.swift"), - encoding: .utf8) - - #expect(source.contains("Form {")) - #expect(source.contains(".formStyle(.grouped)")) - #expect(source.contains("ForEach(model.items.filter")) - let tags = try #require(source.range(of: "TagField(tags: $model.hostTags")) - let storage = try #require(source.range(of: "Picker(\"Store in\", selection: $model.storage)")) - let color = try #require(source.range(of: "ColorTagPicker(selection: $model.importColor")) - let preview = try #require(source.range(of: "if !model.items.isEmpty")) - #expect(storage.lowerBound < color.lowerBound) - #expect(color.lowerBound < tags.lowerBound) - #expect(tags.lowerBound < preview.lowerBound) - #expect(!source.contains("hostColor")) - #expect(!source.contains("keyColor")) - #expect(!source.contains("identityColor")) - #expect(!source.contains("Store password in")) - #expect(!source.contains("private func categorySection")) - } - @Test func droppedSourcesUseTheSameTypeValidationAsTheFilePicker() async throws { let url = try root() diff --git a/TFMTests/SerialFeatureTests.swift b/TFMTests/SerialFeatureTests.swift index 1fd6cec4..a4938f47 100644 --- a/TFMTests/SerialFeatureTests.swift +++ b/TFMTests/SerialFeatureTests.swift @@ -119,7 +119,6 @@ struct SerialFeatureTests { window.select(workspace.id) #expect(pane.kind == .serialSession) - #expect(pane.symbol == "cable.connector") #expect(pane.serialSession?.title == "USB UART") #expect(window.sessionPanes.map(\.pane.id) == [pane.id]) #expect(window.serialSessionPanes.map(\.pane.id) == [pane.id]) diff --git a/TFMTests/TerminalThemeTests.swift b/TFMTests/TerminalThemeTests.swift index 56879417..27261603 100644 --- a/TFMTests/TerminalThemeTests.swift +++ b/TFMTests/TerminalThemeTests.swift @@ -9,12 +9,6 @@ import Testing @testable import Termind struct TerminalThemeTests { - @Test - func defaultThemeBackgroundsMatchMacOSWindowBackgrounds() { - #expect(TermindTerminalPalette.termindLight.background.hex == "FFFFFF") - #expect(TermindTerminalPalette.termindDark.background.hex == "1E1E1E") - } - @Test func unavailableThemesFallBackForBothAppearances() { let light = TermindTerminalThemeCatalog.background(