106 lines
3.5 KiB
Swift
106 lines
3.5 KiB
Swift
import Foundation
|
|
import TermindApplication
|
|
import TermindDomain
|
|
import Testing
|
|
|
|
@Suite("Remote file system policy")
|
|
struct RemoteFileSystemTests {
|
|
@Test func directoryContentsPlacesDirectoriesFirstAndUsesNaturalNameOrder() async throws {
|
|
let fileSystem = RecordingRemoteFileSystem(
|
|
files: [
|
|
Self.remoteFile(path: "/tmp/file10", kind: .file),
|
|
Self.remoteFile(path: "/tmp/folder", kind: .directory),
|
|
Self.remoteFile(path: "/tmp/file2", kind: .file),
|
|
]
|
|
)
|
|
|
|
let files = try await fileSystem.directoryContents(at: "/tmp")
|
|
|
|
#expect(files.map { $0.name } == ["folder", "file2", "file10"])
|
|
}
|
|
|
|
@Test func entryMutationsOwnNamePathAndPermissionRules() async throws {
|
|
let fileSystem = RecordingRemoteFileSystem()
|
|
let file = Self.remoteFile(path: "/srv/old", kind: .file)
|
|
|
|
try await fileSystem.createDirectory(named: "new", in: "/srv")
|
|
try await fileSystem.rename(file, to: "renamed")
|
|
try await fileSystem.setPermissions(of: file, to: 0o17_755)
|
|
try await fileSystem.remove(file)
|
|
|
|
let operations = await fileSystem.operations
|
|
#expect(
|
|
operations == [
|
|
.createDirectory("/srv/new"),
|
|
.rename("/srv/old", "/srv/renamed", false),
|
|
.setPermissions("/srv/old", 0o7_755),
|
|
.removeFile("/srv/old"),
|
|
]
|
|
)
|
|
}
|
|
|
|
@Test func invalidEntryNamesAreRejectedBeforeTransportAccess() async {
|
|
let fileSystem = RecordingRemoteFileSystem()
|
|
let file = Self.remoteFile(path: "/srv/old", kind: .file)
|
|
|
|
await #expect(throws: CocoaError.self) {
|
|
try await fileSystem.rename(file, to: "../escape")
|
|
}
|
|
#expect(await fileSystem.operations.isEmpty)
|
|
}
|
|
|
|
private static func remoteFile(path: String, kind: RemoteFile.Kind) -> RemoteFile {
|
|
RemoteFile(
|
|
path: path,
|
|
name: (path as NSString).lastPathComponent,
|
|
kind: kind,
|
|
size: 0,
|
|
permissions: 0o644,
|
|
ownerUID: 0,
|
|
ownerGID: 0
|
|
)
|
|
}
|
|
}
|
|
|
|
private actor RecordingRemoteFileSystem: RemoteFileSystem {
|
|
enum Operation: Equatable {
|
|
case createDirectory(String)
|
|
case rename(String, String, Bool)
|
|
case setPermissions(String, UInt32)
|
|
case removeFile(String)
|
|
case removeDirectory(String)
|
|
}
|
|
|
|
private let files: [RemoteFile]
|
|
private(set) var operations: [Operation] = []
|
|
|
|
init(files: [RemoteFile] = []) {
|
|
self.files = files
|
|
}
|
|
|
|
func listDirectory(at path: String) async throws -> [RemoteFile] { files }
|
|
func attributes(at path: String) async throws -> RemoteFile? { nil }
|
|
func readFile(at path: String, maximumBytes: Int) async throws -> Data { Data() }
|
|
func writeFile(_ data: Data, at path: String, overwrite: Bool) async throws {}
|
|
|
|
func setPermissions(at path: String, to permissions: UInt32) async throws {
|
|
operations.append(.setPermissions(path, permissions))
|
|
}
|
|
|
|
func createDirectory(at path: String) async throws {
|
|
operations.append(.createDirectory(path))
|
|
}
|
|
|
|
func rename(from source: String, to destination: String, overwrite: Bool) async throws {
|
|
operations.append(.rename(source, destination, overwrite))
|
|
}
|
|
|
|
func removeFile(at path: String) async throws {
|
|
operations.append(.removeFile(path))
|
|
}
|
|
|
|
func removeDirectory(at path: String) async throws {
|
|
operations.append(.removeDirectory(path))
|
|
}
|
|
}
|