Files
clipbored/tests/clipboredtests/ClipboardCloudSyncServiceTests.swift

145 lines
5.5 KiB
Swift
Raw Permalink Normal View History

2026-07-08 01:13:02 -04:00
import Foundation
import XCTest
@testable import ClipBored
final class ClipboardCloudSyncServiceTests: XCTestCase {
private var tempRoot: URL!
private var defaultsSuites: [String] = []
override func setUpWithError() throws {
try super.setUpWithError()
tempRoot = FileManager.default.temporaryDirectory
.appendingPathComponent("clipbored-cloud-sync-tests", isDirectory: true)
.appendingPathComponent(UUID().uuidString, isDirectory: true)
try FileManager.default.createDirectory(at: tempRoot, withIntermediateDirectories: true)
}
override func tearDownWithError() throws {
for suite in defaultsSuites {
UserDefaults(suiteName: suite)?.removePersistentDomain(forName: suite)
}
if let tempRoot {
try? FileManager.default.removeItem(at: tempRoot)
}
defaultsSuites = []
tempRoot = nil
try super.tearDownWithError()
}
func testUnavailableContainerReportsStatusAndThrows() {
let service = ClipboardCloudSyncService(containerProvider: { nil })
let status = service.status()
XCTAssertFalse(status.isAvailable)
XCTAssertNil(status.archiveURL)
XCTAssertTrue(status.message.contains("iCloud Sync is unavailable"))
XCTAssertThrowsError(try service.syncArchiveURL()) { error in
XCTAssertEqual(error as? ClipboardCloudSyncError, .unavailable)
}
}
func testPushWritesArchiveToPrivateDocumentsFolder() throws {
let environment = try makeStoreEnvironment(named: "source")
environment.store.upsert(makeItem("cloud note", created: Date(timeIntervalSince1970: 10)))
environment.store.flushPersistenceForTesting()
let cloudRoot = tempRoot.appendingPathComponent("cloud", isDirectory: true)
let service = ClipboardCloudSyncService(containerProvider: { cloudRoot })
let summary = try service.push(store: environment.store)
let archiveURL = try service.syncArchiveURL()
let status = service.status()
XCTAssertEqual(summary.itemCount, 1)
XCTAssertEqual(
archiveURL,
cloudRoot
.appendingPathComponent("Documents", isDirectory: true)
.appendingPathComponent(AppConfiguration.appName, isDirectory: true)
.appendingPathComponent(ClipboardCloudSyncService.archiveFileName)
)
XCTAssertTrue(FileManager.default.fileExists(atPath: archiveURL.path))
XCTAssertEqual(try posixPermissions(archiveURL), 0o600)
XCTAssertTrue(status.isAvailable)
XCTAssertEqual(status.archiveURL, archiveURL)
XCTAssertNotNil(status.lastModifiedAt)
}
func testPullImportsExistingCloudArchiveIntoAnotherStore() throws {
let source = try makeStoreEnvironment(named: "source")
let sourceItem = makeItem("shared through icloud", created: Date(timeIntervalSince1970: 20))
source.store.upsert(sourceItem)
source.store.flushPersistenceForTesting()
let cloudRoot = tempRoot.appendingPathComponent("cloud", isDirectory: true)
let service = ClipboardCloudSyncService(containerProvider: { cloudRoot })
try service.push(store: source.store)
let destination = try makeStoreEnvironment(named: "destination")
let summary = try service.pull(store: destination.store)
destination.store.flushPersistenceForTesting()
XCTAssertEqual(summary.itemCount, 1)
XCTAssertEqual(destination.store.items.count, 1)
XCTAssertEqual(destination.store.items.first?.id, sourceItem.id)
XCTAssertEqual(destination.store.items.first?.payload, "shared through icloud")
}
func testPullWithoutRemoteArchiveThrowsNoRemoteArchive() throws {
let destination = try makeStoreEnvironment(named: "destination")
let cloudRoot = tempRoot.appendingPathComponent("empty-cloud", isDirectory: true)
let service = ClipboardCloudSyncService(containerProvider: { cloudRoot })
XCTAssertThrowsError(try service.pull(store: destination.store)) { error in
guard case ClipboardCloudSyncError.noRemoteArchive(let url) = error else {
return XCTFail("Expected noRemoteArchive, got \(error)")
}
XCTAssertEqual(url.lastPathComponent, ClipboardCloudSyncService.archiveFileName)
}
}
private func makeStoreEnvironment(named name: String) throws -> (settings: SettingsModel, store: ClipboardStore) {
let suiteName = "com.clipbored.cloudsync.\(name).\(UUID().uuidString)"
defaultsSuites.append(suiteName)
let defaults = try XCTUnwrap(UserDefaults(suiteName: suiteName))
defaults.removePersistentDomain(forName: suiteName)
let settings = SettingsModel(defaults: defaults)
settings.maxHistoryItems = 50
settings.historyRetention = .forever
let baseURL = tempRoot.appendingPathComponent(name, isDirectory: true)
let encryptionService = ClipboardEncryptionService(keyProvider: { nil })
let cacheService = ClipboardCacheService(baseURL: baseURL, encryptionService: encryptionService)
let store = ClipboardStore(
settings: settings,
cacheService: cacheService,
baseURL: baseURL,
encryptionService: encryptionService
)
return (settings, store)
}
private func makeItem(_ payload: String, created: Date) -> ClipboardItem {
ClipboardItem(
id: UUID(),
kind: .text,
displayText: payload,
payload: payload,
payloadHash: String(payload.hashValue),
createdAt: created,
lastUsedAt: created,
useCount: 1,
sourceApp: nil,
imagePath: nil,
thumbnailPath: nil,
isPinned: false
)
}
private func posixPermissions(_ url: URL) throws -> Int {
let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
return try XCTUnwrap(attributes[.posixPermissions] as? Int)
}
}