mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-14 20:26:05 +00:00
155 lines
6.5 KiB
Swift
155 lines
6.5 KiB
Swift
import Foundation
|
|
import HapiClient
|
|
import HapiProtocol
|
|
import Testing
|
|
|
|
private struct DirectoryBrowserCall: Equatable, Sendable {
|
|
let machineId: String
|
|
let path: String
|
|
let includeHidden: Bool
|
|
}
|
|
|
|
private actor FakeMachineDirectoryRequester: MachineDirectoryRequesting {
|
|
private(set) var calls: [DirectoryBrowserCall] = []
|
|
|
|
func listMachineDirectory(
|
|
machineId: String,
|
|
path: String,
|
|
includeHidden: Bool
|
|
) async throws -> MachineListDirectoryResponse {
|
|
calls.append(DirectoryBrowserCall(
|
|
machineId: machineId,
|
|
path: path,
|
|
includeHidden: includeHidden
|
|
))
|
|
return MachineListDirectoryResponse(
|
|
success: true,
|
|
entries: [
|
|
MachineDirectoryEntry(name: "repo", type: .directory),
|
|
MachineDirectoryEntry(name: "README.md", type: .file),
|
|
]
|
|
)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private func directoryBrowserEventually(
|
|
timeout: Duration = .seconds(5),
|
|
_ condition: @MainActor () -> Bool
|
|
) async -> Bool {
|
|
let clock = ContinuousClock()
|
|
let deadline = clock.now.advanced(by: timeout)
|
|
while clock.now < deadline {
|
|
if condition() { return true }
|
|
try? await Task.sleep(for: .milliseconds(10))
|
|
}
|
|
return condition()
|
|
}
|
|
|
|
@Suite("RemoteDirectoryBrowserModel")
|
|
@MainActor
|
|
struct RemoteDirectoryBrowserModelTests {
|
|
@Test func defaultDirectoryIsIndependentOfBrowseBoundaries() {
|
|
var machine = Machine(
|
|
id: "m", namespace: "test", seq: 1, createdAt: 0, updatedAt: 0,
|
|
active: true, activeAt: 0,
|
|
metadata: MachineMetadata(host: "m", platform: "linux", happyCliVersion: "test", homeDir: "/home/dev"),
|
|
metadataVersion: 1, runnerStateVersion: 0
|
|
)
|
|
#expect(RemoteDirectoryPath.browseRoots(for: machine).isEmpty)
|
|
#expect(RemoteDirectoryPath.defaultDirectory(for: machine) == "/home/dev")
|
|
machine.metadata?.workspaceRoots = ["/data", "/work"]
|
|
#expect(RemoteDirectoryPath.browseRoots(for: machine) == ["/data", "/work"])
|
|
#expect(RemoteDirectoryPath.defaultDirectory(for: machine) == "/data")
|
|
#expect(RemoteDirectoryPath.expandHome("~/repo", homeDirectory: "/remote/home") == "/remote/home/repo")
|
|
#expect(RemoteDirectoryPath.expandHome("~", homeDirectory: "/remote/home") == "/remote/home")
|
|
#expect(RemoteDirectoryPath.expandHome("~\\repo", homeDirectory: "D:\\Users\\dev") == "D:\\Users\\dev\\repo")
|
|
#expect(RemoteDirectoryPath.filesystemRoot("D:\\Users\\dev") == "D:\\")
|
|
#expect(RemoteDirectoryPath.filesystemRoot("\\\\server\\share\\repo") == "\\\\server\\share")
|
|
#expect(!RemoteDirectoryPath.allowsBrowsing(path: "relative/path", roots: []))
|
|
}
|
|
|
|
@Test func unrestrictedBrowserPreservesExternalInputAndNavigatesAboveHome() async {
|
|
let requester = FakeMachineDirectoryRequester()
|
|
let model = RemoteDirectoryBrowserModel(requester: requester)
|
|
model.open(machineId: "m", roots: [], initialPath: "/data/github/hapi", defaultPath: "/home/dev")
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.path == "/data/github/hapi")
|
|
#expect(model.breadcrumbs.map(\.path) == ["/", "/data", "/data/github", "/data/github/hapi"])
|
|
model.navigate(to: "/home/dev")
|
|
model.navigateUp()
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.path == "/home")
|
|
model.navigate(to: "/")
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(!model.canGoUp)
|
|
model.open(machineId: "m", roots: [], initialPath: "", defaultPath: "/home/dev")
|
|
#expect(model.path == "/home/dev")
|
|
model.close()
|
|
}
|
|
|
|
@Test func configuredRootsRemainBoundariesAndSupportSwitchingRoots() async {
|
|
let requester = FakeMachineDirectoryRequester()
|
|
let model = RemoteDirectoryBrowserModel(requester: requester)
|
|
model.open(machineId: "m", roots: ["/data", "/work"], initialPath: "/home/dev", defaultPath: "/home/dev")
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.path == "/data")
|
|
#expect(!model.canGoUp)
|
|
model.navigate(to: "/work/repo")
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.breadcrumbs.map(\.path) == ["/work", "/work/repo"])
|
|
model.navigateUp()
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.path == "/work")
|
|
#expect(!model.canGoUp)
|
|
model.close()
|
|
}
|
|
|
|
@Test func pathBoundariesSupportPosixDriveAndUNCPaths() {
|
|
#expect(RemoteDirectoryPath.isWithinRoot(path: "/workspace/repo", root: "/workspace"))
|
|
#expect(!RemoteDirectoryPath.isWithinRoot(path: "/workspace-other/repo", root: "/workspace"))
|
|
#expect(RemoteDirectoryPath.isWithinRoot(path: "/workspace", root: "/"))
|
|
#expect(RemoteDirectoryPath.isWithinRoot(path: "c:\\Work\\Repo", root: "C:\\work"))
|
|
#expect(!RemoteDirectoryPath.isWithinRoot(
|
|
path: "C:\\workspace-other",
|
|
root: "C:\\workspace"
|
|
))
|
|
#expect(RemoteDirectoryPath.isWithinRoot(
|
|
path: "\\\\SERVER\\Share\\Repo",
|
|
root: "\\\\server\\share"
|
|
))
|
|
#expect(RemoteDirectoryPath.parent("C:\\Users") == "C:\\")
|
|
#expect(
|
|
RemoteDirectoryPath.parent("\\\\server\\share\\repo")
|
|
== "\\\\server\\share"
|
|
)
|
|
}
|
|
|
|
@Test func modelOwnsNavigationLoadingAndHiddenDirectoryState() async {
|
|
let requester = FakeMachineDirectoryRequester()
|
|
let model = RemoteDirectoryBrowserModel(requester: requester)
|
|
|
|
model.open(machineId: "machine-1", roots: ["/workspace"], initialPath: "/workspace")
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(model.entries.map(\.name) == ["repo"])
|
|
|
|
model.navigate(to: "/workspace-other")
|
|
try? await Task.sleep(for: .milliseconds(20))
|
|
#expect(await requester.calls.count == 1)
|
|
|
|
model.navigateEntry("repo")
|
|
#expect(await directoryBrowserEventually { !model.isLoading && model.path == "/workspace/repo" })
|
|
#expect(model.canGoUp)
|
|
|
|
model.setIncludeHidden(true)
|
|
#expect(await directoryBrowserEventually { !model.isLoading })
|
|
#expect(await requester.calls.last?.includeHidden == true)
|
|
|
|
model.navigateUp()
|
|
#expect(await directoryBrowserEventually { !model.isLoading && model.path == "/workspace" })
|
|
|
|
model.close()
|
|
#expect(!model.isPresented)
|
|
}
|
|
}
|