Files
suixinkan_uikit/suixinkanTests/CloudTransferManagerTests.swift

186 lines
6.3 KiB
Swift

//
// CloudTransferManagerTests.swift
// suixinkanTests
//
import Foundation
import XCTest
@testable import suixinkan
@MainActor
///
final class CloudTransferManagerTests: XCTestCase {
func testUploadRegistersFileAndRemovesCompletedTask() async throws {
let store = MemoryTransferStore()
let uploader = FakeCloudUploader(url: "https://cdn/a.jpg")
let api = TransferMockCloudDriveAPI()
let manager = CloudTransferManager(
store: store,
uploader: uploader,
api: api,
downloader: FakeDownloader(),
photoSaver: FakePhotoSaver(),
scenicIdProvider: { 10 }
)
let fileURL = try makeTempFile(name: "a.jpg")
var completedFolder: Int?
manager.onUploadCompleted = { completedFolder = $0 }
manager.addUploadTask(localFileURL: fileURL, fileName: "a.jpg", fileType: 2, fileSize: 3, parentFolderId: 6)
try await waitUntil { manager.uploadTasks.isEmpty }
XCTAssertEqual(api.uploadCalls.first?.parentFolderId, 6)
XCTAssertEqual(api.uploadCalls.first?.fileUrl, "https://cdn/a.jpg")
XCTAssertEqual(completedFolder, 6)
XCTAssertTrue(store.saved.last?.allSatisfy { $0.kind != .upload } == true)
}
func testUploadFailureKeepsFailedTask() async throws {
let uploader = FakeCloudUploader(error: TestError.failed)
let manager = CloudTransferManager(
store: MemoryTransferStore(),
uploader: uploader,
api: TransferMockCloudDriveAPI(),
downloader: FakeDownloader(),
photoSaver: FakePhotoSaver(),
scenicIdProvider: { 10 }
)
let fileURL = try makeTempFile(name: "a.jpg")
manager.addUploadTask(localFileURL: fileURL, fileName: "a.jpg", fileType: 2, fileSize: 3, parentFolderId: 0)
try await waitUntil { manager.uploadTasks.first?.status == .failed }
XCTAssertEqual(manager.uploadTasks.first?.status, .failed)
}
func testDownloadCompletesAndCanCancel() async throws {
let downloader = FakeDownloader()
let saver = FakePhotoSaver()
let manager = CloudTransferManager(
store: MemoryTransferStore(),
uploader: FakeCloudUploader(url: "unused"),
api: TransferMockCloudDriveAPI(),
downloader: downloader,
photoSaver: saver,
scenicIdProvider: { 10 }
)
manager.addDownloadTask(fileURL: "https://cdn/a.jpg", fileName: "a.jpg", fileType: 2)
try await waitUntil { manager.downloadTasks.first?.status == .completed }
XCTAssertEqual(downloader.downloadCalls.first?.remoteURL, "https://cdn/a.jpg")
XCTAssertEqual(saver.saved.count, 1)
XCTAssertEqual(manager.downloadTasks.first?.progress, 100)
let id = try XCTUnwrap(manager.downloadTasks.first?.id)
manager.cancelDownload(id: id)
XCTAssertTrue(manager.downloadTasks.isEmpty)
}
private func makeTempFile(name: String) throws -> URL {
let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + "_" + name)
try Data([1, 2, 3]).write(to: url)
return url
}
private func waitUntil(
timeout: TimeInterval = 2,
condition: @escaping () -> Bool
) async throws {
let deadline = Date().addingTimeInterval(timeout)
while !condition() {
if Date() > deadline {
XCTFail("Timed out waiting for condition")
return
}
try await Task.sleep(nanoseconds: 20_000_000)
}
}
}
private final class MemoryTransferStore: CloudTransferTaskStoring {
var tasks: [CloudTransferTask] = []
var saved: [[CloudTransferTask]] = []
func loadTasks() -> [CloudTransferTask] {
tasks
}
func saveTasks(_ tasks: [CloudTransferTask]) {
self.tasks = tasks
saved.append(tasks)
}
}
@MainActor
private final class FakeCloudUploader: CloudDriveUploading {
let url: String
let error: Error?
init(url: String = "", error: Error? = nil) {
self.url = url
self.error = error
}
func uploadCloudDriveFile(data: Data, fileName: String, fileType: Int, scenicId: Int, onProgress: @escaping (Int) -> Void) async throws -> String {
if let error { throw error }
onProgress(50)
onProgress(100)
return url
}
}
private final class FakeDownloader: CloudFileDownloading {
var downloadCalls: [(remoteURL: String, fileName: String)] = []
func downloadFile(remoteURL: String, fileName: String, onProgress: @escaping @Sendable (Int) -> Void) async throws -> URL {
downloadCalls.append((remoteURL, fileName))
onProgress(40)
let url = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString + "_" + fileName)
try Data([1]).write(to: url)
onProgress(100)
return url
}
}
private final class FakePhotoSaver: CloudPhotoSaving {
var saved: [(fileURL: URL, fileType: Int)] = []
func saveToPhotoLibrary(fileURL: URL, fileType: Int) async throws {
saved.append((fileURL, fileType))
}
}
@MainActor
private final class TransferMockCloudDriveAPI: CloudDriveServing {
var uploadCalls: [(parentFolderId: Int, fileUrl: String, fileName: String)] = []
func cloudFileList(parentFolderId: Int, name: String, type: Int, orderBy: Int, page: Int, pageSize: Int) async throws -> CloudFileListResponse {
CloudFileListResponse()
}
func createCloudFolder(parentFolderId: Int, name: String) async throws {}
func uploadCloudFile(parentFolderId: Int, fileUrl: String, fileName: String) async throws {
uploadCalls.append((parentFolderId, fileUrl, fileName))
}
func deleteCloudFiles(_ request: CloudFileDeleteRequest) async throws {}
func moveCloudFiles(_ request: CloudFileMoveRequest) async throws {}
func checkUploadPermission() async throws -> CloudUploadPermissionResponse {
CloudUploadPermissionResponse(canUpload: true, reason: "")
}
func modifyCloudFolderName(fileId: Int, name: String) async throws {}
func modifyCloudFileName(fileId: Int, fileName: String) async throws {}
}
private enum TestError: LocalizedError {
case failed
var errorDescription: String? { "failed" }
}