diff --git a/CryptomatorFileProvider/CloudTask/CloudTask.swift b/CryptomatorFileProvider/CloudTask/CloudTask.swift index 4ed46f1ff..4f6b13c69 100644 --- a/CryptomatorFileProvider/CloudTask/CloudTask.swift +++ b/CryptomatorFileProvider/CloudTask/CloudTask.swift @@ -11,10 +11,6 @@ import Foundation protocol CloudTask { var itemMetadata: ItemMetadata { get } -} - -extension CloudTask { - var cloudPath: CloudPath { - return itemMetadata.cloudPath - } + /// Snapshot captured at task construction; survives concurrent local renames of the same row. + var cloudPath: CloudPath { get } } diff --git a/CryptomatorFileProvider/CloudTask/DeletionTask.swift b/CryptomatorFileProvider/CloudTask/DeletionTask.swift index e612216cb..36634d893 100644 --- a/CryptomatorFileProvider/CloudTask/DeletionTask.swift +++ b/CryptomatorFileProvider/CloudTask/DeletionTask.swift @@ -6,14 +6,14 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // -import GRDB +import CryptomatorCloudAccessCore -struct DeletionTask: CloudTask, FetchableRecord, Decodable { +struct DeletionTask: CloudTask { let taskRecord: DeletionTaskRecord let itemMetadata: ItemMetadata + let cloudPath: CloudPath - enum CodingKeys: String, CodingKey { - case taskRecord = "deletionTask" - case itemMetadata + func with(cloudPath: CloudPath) -> DeletionTask { + return DeletionTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath) } } diff --git a/CryptomatorFileProvider/CloudTask/DownloadTask.swift b/CryptomatorFileProvider/CloudTask/DownloadTask.swift index e125a7e11..37bc6a1cf 100644 --- a/CryptomatorFileProvider/CloudTask/DownloadTask.swift +++ b/CryptomatorFileProvider/CloudTask/DownloadTask.swift @@ -6,17 +6,17 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Foundation -import GRDB struct DownloadTask: CloudTask { let taskRecord: DownloadTaskRecord let itemMetadata: ItemMetadata + let cloudPath: CloudPath let onURLSessionTaskCreation: URLSessionTaskCreationClosure? - enum CodingKeys: String, CodingKey { - case taskRecord = "downloadTask" - case itemMetadata + func with(cloudPath: CloudPath) -> DownloadTask { + return DownloadTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: onURLSessionTaskCreation) } } diff --git a/CryptomatorFileProvider/CloudTask/FolderCreationTask.swift b/CryptomatorFileProvider/CloudTask/FolderCreationTask.swift index c4b402d81..f95d757b9 100644 --- a/CryptomatorFileProvider/CloudTask/FolderCreationTask.swift +++ b/CryptomatorFileProvider/CloudTask/FolderCreationTask.swift @@ -6,8 +6,13 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // -import Foundation +import CryptomatorCloudAccessCore struct FolderCreationTask: CloudTask { let itemMetadata: ItemMetadata + let cloudPath: CloudPath + + func with(cloudPath: CloudPath) -> FolderCreationTask { + return FolderCreationTask(itemMetadata: itemMetadata, cloudPath: cloudPath) + } } diff --git a/CryptomatorFileProvider/CloudTask/ItemEnumerationTask.swift b/CryptomatorFileProvider/CloudTask/ItemEnumerationTask.swift index a01000d00..ad47e4dd5 100644 --- a/CryptomatorFileProvider/CloudTask/ItemEnumerationTask.swift +++ b/CryptomatorFileProvider/CloudTask/ItemEnumerationTask.swift @@ -6,15 +6,14 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // -import Foundation -import GRDB +import CryptomatorCloudAccessCore struct ItemEnumerationTask: CloudTask { let taskRecord: ItemEnumerationTaskRecord let itemMetadata: ItemMetadata + let cloudPath: CloudPath - enum CodingKeys: String, CodingKey { - case taskRecord = "itemEnumerationTask" - case itemMetadata + func with(cloudPath: CloudPath) -> ItemEnumerationTask { + return ItemEnumerationTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath) } } diff --git a/CryptomatorFileProvider/CloudTask/ReparentTask.swift b/CryptomatorFileProvider/CloudTask/ReparentTask.swift index d671bad13..56d027d12 100644 --- a/CryptomatorFileProvider/CloudTask/ReparentTask.swift +++ b/CryptomatorFileProvider/CloudTask/ReparentTask.swift @@ -6,14 +6,14 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // -import GRDB +import CryptomatorCloudAccessCore -struct ReparentTask: CloudTask, FetchableRecord, Decodable { +struct ReparentTask: CloudTask { let taskRecord: ReparentTaskRecord let itemMetadata: ItemMetadata + let cloudPath: CloudPath - enum CodingKeys: String, CodingKey { - case taskRecord = "reparentTask" - case itemMetadata + func with(cloudPath: CloudPath, taskRecord: ReparentTaskRecord) -> ReparentTask { + return ReparentTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath) } } diff --git a/CryptomatorFileProvider/CloudTask/UploadTask.swift b/CryptomatorFileProvider/CloudTask/UploadTask.swift index ac604cc9a..729c0003d 100644 --- a/CryptomatorFileProvider/CloudTask/UploadTask.swift +++ b/CryptomatorFileProvider/CloudTask/UploadTask.swift @@ -6,11 +6,16 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Foundation -import GRDB struct UploadTask: CloudTask { let taskRecord: UploadTaskRecord let itemMetadata: ItemMetadata + let cloudPath: CloudPath let onURLSessionTaskCreation: URLSessionTaskCreationClosure? + + func with(cloudPath: CloudPath) -> UploadTask { + return UploadTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: onURLSessionTaskCreation) + } } diff --git a/CryptomatorFileProvider/DB/DatabaseHelper.swift b/CryptomatorFileProvider/DB/DatabaseHelper.swift index 0a182e01f..98ef24364 100644 --- a/CryptomatorFileProvider/DB/DatabaseHelper.swift +++ b/CryptomatorFileProvider/DB/DatabaseHelper.swift @@ -209,6 +209,32 @@ public struct DatabaseHelper: DatabaseHelping { ) """) } + migrator.registerMigration("v5", foreignKeyChecks: .deferred) { db in + // SQLite refuses DROP COLUMN on UNIQUE-constrained columns, so we must rebuild to drop `cloudPath`. + try db.execute(sql: """ + CREATE TABLE itemMetadata_tmp ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + type TEXT NOT NULL, + size INTEGER, + parentID INTEGER REFERENCES itemMetadata(id) ON DELETE CASCADE, + lastModifiedDate DATE, + statusCode TEXT NOT NULL, + isPlaceholderItem BOOLEAN NOT NULL DEFAULT 0, + isMaybeOutdated BOOLEAN NOT NULL DEFAULT 0, + favoriteRank INTEGER, + tagData BLOB, + lastEnumeratedAt DATE + ) + """) + try db.execute(sql: """ + INSERT INTO itemMetadata_tmp (id, name, type, size, parentID, lastModifiedDate, statusCode, isPlaceholderItem, isMaybeOutdated, favoriteRank, tagData, lastEnumeratedAt) + SELECT id, name, type, size, parentID, lastModifiedDate, statusCode, isPlaceholderItem, isMaybeOutdated, favoriteRank, tagData, lastEnumeratedAt FROM itemMetadata + """) + try db.execute(sql: "DROP TABLE itemMetadata") + try db.execute(sql: "ALTER TABLE itemMetadata_tmp RENAME TO itemMetadata") + try db.execute(sql: "CREATE INDEX itemMetadata_parentID_idx ON itemMetadata(parentID)") + } try migrator.migrate(dbWriter) } diff --git a/CryptomatorFileProvider/DB/DeletionTaskDBManager.swift b/CryptomatorFileProvider/DB/DeletionTaskDBManager.swift index 0c786ba0c..6bca6caf5 100644 --- a/CryptomatorFileProvider/DB/DeletionTaskDBManager.swift +++ b/CryptomatorFileProvider/DB/DeletionTaskDBManager.swift @@ -6,11 +6,12 @@ // Copyright © 2020 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Foundation import GRDB protocol DeletionTaskManager { - func createTaskRecord(for item: ItemMetadata) throws -> DeletionTaskRecord + func createTaskRecord(for item: ItemMetadata, cloudPath: CloudPath) throws -> DeletionTaskRecord func getTaskRecord(for id: Int64) throws -> DeletionTaskRecord func removeTaskRecord(_ task: DeletionTaskRecord) throws func getTaskRecordsForItemsWhichWere(in parentID: Int64) throws -> [DeletionTaskRecord] @@ -27,9 +28,12 @@ class DeletionTaskDBManager: DeletionTaskManager { } } - func createTaskRecord(for item: ItemMetadata) throws -> DeletionTaskRecord { - try database.write { db in - let task = DeletionTaskRecord(correspondingItem: item.id!, cloudPath: item.cloudPath, parentID: item.parentID, itemType: item.type) + func createTaskRecord(for item: ItemMetadata, cloudPath: CloudPath) throws -> DeletionTaskRecord { + guard let id = item.id else { + throw DBManagerError.nonSavedItemMetadata + } + return try database.write { db in + let task = DeletionTaskRecord(correspondingItem: id, cloudPath: cloudPath, parentID: item.parentID, itemType: item.type) try task.save(db) return task } @@ -63,7 +67,7 @@ class DeletionTaskDBManager: DeletionTaskManager { guard let itemMetadata = try taskRecord.itemMetadata.fetchOne(db) else { throw DBManagerError.missingItemMetadata } - return DeletionTask(taskRecord: taskRecord, itemMetadata: itemMetadata) + return DeletionTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: taskRecord.cloudPath) } } } diff --git a/CryptomatorFileProvider/DB/DownloadTaskDBManager.swift b/CryptomatorFileProvider/DB/DownloadTaskDBManager.swift index ee94e818d..18bcb79e8 100644 --- a/CryptomatorFileProvider/DB/DownloadTaskDBManager.swift +++ b/CryptomatorFileProvider/DB/DownloadTaskDBManager.swift @@ -6,6 +6,7 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Foundation import GRDB @@ -16,19 +17,25 @@ protocol DownloadTaskManager { class DownloadTaskDBManager: DownloadTaskManager { private let database: DatabaseWriter + private let itemMetadataManager: ItemMetadataManager - init(database: DatabaseWriter) throws { + init(database: DatabaseWriter, itemMetadataManager: ItemMetadataManager) throws { self.database = database + self.itemMetadataManager = itemMetadataManager _ = try database.write { db in - try ItemEnumerationTaskRecord.deleteAll(db) + try DownloadTaskRecord.deleteAll(db) } } func createTask(for item: ItemMetadata, replaceExisting: Bool, localURL: URL, onURLSessionTaskCreation: URLSessionTaskCreationClosure?) throws -> DownloadTask { - try database.write { db in - let taskRecord = DownloadTaskRecord(correspondingItem: item.id!, replaceExisting: replaceExisting, localURL: localURL) + guard let id = item.id else { + throw DBManagerError.nonSavedItemMetadata + } + let cloudPath = try itemMetadataManager.getCloudPath(for: id) + return try database.write { db in + let taskRecord = DownloadTaskRecord(correspondingItem: id, replaceExisting: replaceExisting, localURL: localURL) try taskRecord.save(db) - return DownloadTask(taskRecord: taskRecord, itemMetadata: item, onURLSessionTaskCreation: onURLSessionTaskCreation) + return DownloadTask(taskRecord: taskRecord, itemMetadata: item, cloudPath: cloudPath, onURLSessionTaskCreation: onURLSessionTaskCreation) } } diff --git a/CryptomatorFileProvider/DB/ItemEnumerationTaskDBManager.swift b/CryptomatorFileProvider/DB/ItemEnumerationTaskDBManager.swift index aaab7c6e5..aafa33d91 100644 --- a/CryptomatorFileProvider/DB/ItemEnumerationTaskDBManager.swift +++ b/CryptomatorFileProvider/DB/ItemEnumerationTaskDBManager.swift @@ -6,6 +6,7 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Foundation import GRDB @@ -16,19 +17,25 @@ protocol ItemEnumerationTaskManager { class ItemEnumerationTaskDBManager: ItemEnumerationTaskManager { private let database: DatabaseWriter + private let itemMetadataManager: ItemMetadataManager - init(database: DatabaseWriter) throws { + init(database: DatabaseWriter, itemMetadataManager: ItemMetadataManager) throws { self.database = database + self.itemMetadataManager = itemMetadataManager _ = try database.write { db in try ItemEnumerationTaskRecord.deleteAll(db) } } func createTask(for item: ItemMetadata, pageToken: String?) throws -> ItemEnumerationTask { - try database.write { db in - let taskRecord = ItemEnumerationTaskRecord(correspondingItem: item.id!, pageToken: pageToken) + guard let id = item.id else { + throw DBManagerError.nonSavedItemMetadata + } + let cloudPath = try itemMetadataManager.getCloudPath(for: id) + return try database.write { db in + let taskRecord = ItemEnumerationTaskRecord(correspondingItem: id, pageToken: pageToken) try taskRecord.save(db) - return ItemEnumerationTask(taskRecord: taskRecord, itemMetadata: item) + return ItemEnumerationTask(taskRecord: taskRecord, itemMetadata: item, cloudPath: cloudPath) } } diff --git a/CryptomatorFileProvider/DB/ItemMetadata.swift b/CryptomatorFileProvider/DB/ItemMetadata.swift index 2625dff18..c21c1a02e 100644 --- a/CryptomatorFileProvider/DB/ItemMetadata.swift +++ b/CryptomatorFileProvider/DB/ItemMetadata.swift @@ -26,7 +26,6 @@ public class ItemMetadata: Record, Codable { var parentID: Int64 var lastModifiedDate: Date? var statusCode: ItemStatus - var cloudPath: CloudPath var isPlaceholderItem: Bool var isMaybeOutdated: Bool var favoriteRank: Int64? @@ -41,7 +40,6 @@ public class ItemMetadata: Record, Codable { self.parentID = row[Columns.parentID] self.lastModifiedDate = row[Columns.lastModifiedDate] self.statusCode = row[Columns.statusCode] - self.cloudPath = row[Columns.cloudPath] self.isPlaceholderItem = row[Columns.isPlaceholderItem] self.isMaybeOutdated = row[Columns.isMaybeOutdated] self.favoriteRank = row[Columns.favoriteRank] @@ -51,10 +49,10 @@ public class ItemMetadata: Record, Codable { } convenience init(item: CloudItemMetadata, withParentID parentID: Int64, isPlaceholderItem: Bool = false) { - self.init(name: item.name, type: item.itemType, size: item.size, parentID: parentID, lastModifiedDate: item.lastModifiedDate, statusCode: .isUploaded, cloudPath: item.cloudPath, isPlaceholderItem: isPlaceholderItem) + self.init(name: item.name, type: item.itemType, size: item.size, parentID: parentID, lastModifiedDate: item.lastModifiedDate, statusCode: .isUploaded, isPlaceholderItem: isPlaceholderItem) } - init(id: Int64? = nil, name: String, type: CloudItemType, size: Int?, parentID: Int64, lastModifiedDate: Date?, statusCode: ItemStatus, cloudPath: CloudPath, isPlaceholderItem: Bool, isCandidateForCacheCleanup: Bool = false, favoriteRank: Int64? = nil, tagData: Data? = nil, lastEnumeratedAt: Date? = nil) { + init(id: Int64? = nil, name: String, type: CloudItemType, size: Int?, parentID: Int64, lastModifiedDate: Date?, statusCode: ItemStatus, isPlaceholderItem: Bool, isCandidateForCacheCleanup: Bool = false, favoriteRank: Int64? = nil, tagData: Data? = nil, lastEnumeratedAt: Date? = nil) { self.id = id self.name = name self.type = type @@ -62,7 +60,6 @@ public class ItemMetadata: Record, Codable { self.parentID = parentID self.lastModifiedDate = lastModifiedDate self.statusCode = statusCode - self.cloudPath = cloudPath self.isPlaceholderItem = isPlaceholderItem self.isMaybeOutdated = isCandidateForCacheCleanup self.favoriteRank = favoriteRank @@ -83,7 +80,6 @@ public class ItemMetadata: Record, Codable { container[Columns.parentID] = parentID container[Columns.lastModifiedDate] = lastModifiedDate container[Columns.statusCode] = statusCode - container[Columns.cloudPath] = cloudPath container[Columns.isPlaceholderItem] = isPlaceholderItem container[Columns.isMaybeOutdated] = isMaybeOutdated container[Columns.favoriteRank] = favoriteRank @@ -92,7 +88,7 @@ public class ItemMetadata: Record, Codable { } enum Columns: String, ColumnExpression { - case id, name, type, size, parentID, lastModifiedDate, statusCode, cloudPath, isPlaceholderItem, isMaybeOutdated, favoriteRank, tagData, lastEnumeratedAt + case id, name, type, size, parentID, lastModifiedDate, statusCode, isPlaceholderItem, isMaybeOutdated, favoriteRank, tagData, lastEnumeratedAt } static func filterWorkingSet() -> QueryInterfaceRequest { @@ -103,6 +99,6 @@ public class ItemMetadata: Record, Codable { extension ItemStatus: DatabaseValueConvertible {} extension ItemMetadata: Equatable { public static func == (lhs: ItemMetadata, rhs: ItemMetadata) -> Bool { - lhs.id == rhs.id && lhs.name == rhs.name && lhs.type == rhs.type && lhs.size == rhs.size && lhs.parentID == rhs.parentID && lhs.lastModifiedDate == rhs.lastModifiedDate && lhs.statusCode == rhs.statusCode && lhs.cloudPath == rhs.cloudPath && lhs.isPlaceholderItem == rhs.isPlaceholderItem && lhs.isMaybeOutdated == rhs.isMaybeOutdated && lhs.favoriteRank == rhs.favoriteRank && lhs.tagData == rhs.tagData && lhs.lastEnumeratedAt == rhs.lastEnumeratedAt + lhs.id == rhs.id && lhs.name == rhs.name && lhs.type == rhs.type && lhs.size == rhs.size && lhs.parentID == rhs.parentID && lhs.lastModifiedDate == rhs.lastModifiedDate && lhs.statusCode == rhs.statusCode && lhs.isPlaceholderItem == rhs.isPlaceholderItem && lhs.isMaybeOutdated == rhs.isMaybeOutdated && lhs.favoriteRank == rhs.favoriteRank && lhs.tagData == rhs.tagData && lhs.lastEnumeratedAt == rhs.lastEnumeratedAt } } diff --git a/CryptomatorFileProvider/DB/ItemMetadataDBManager.swift b/CryptomatorFileProvider/DB/ItemMetadataDBManager.swift index 0f7ddc189..14e6e7ea2 100644 --- a/CryptomatorFileProvider/DB/ItemMetadataDBManager.swift +++ b/CryptomatorFileProvider/DB/ItemMetadataDBManager.swift @@ -15,6 +15,8 @@ protocol ItemMetadataManager { func cacheMetadata(_ metadata: ItemMetadata) throws func updateMetadata(_ metadata: ItemMetadata) throws func cacheMetadata(_ metadataList: [ItemMetadata]) throws + /// Resolves the cloud path by walking the `parentID` chain to root. + func getCloudPath(for id: Int64) throws -> CloudPath /** Returns the item metadata that has the same path. @@ -32,7 +34,7 @@ protocol ItemMetadataManager { func removeItemMetadata(_ ids: [Int64]) throws func getCachedMetadata(forIDs ids: [Int64]) throws -> [ItemMetadata] /** - Returns the items that have the item as parent because of its cloud path. This also includes all subfolders including their items. + Returns every descendant of the given folder, walking the `parentID` chain downward (deep, not just direct children). */ func getAllCachedMetadata(inside parent: ItemMetadata) throws -> [ItemMetadata] // Returns all items that have a `favoriteRank` or `tagData`. @@ -69,9 +71,15 @@ class ItemMetadataDBManager: ItemMetadataManager { } } + func getCloudPath(for id: Int64) throws -> CloudPath { + return try database.read { db in + try resolveCloudPath(for: id, database: db) + } + } + func getCachedMetadata(for cloudPath: CloudPath) throws -> ItemMetadata? { return try database.read { db in - return try ItemMetadata.filter(ItemMetadata.Columns.cloudPath.lowercased == cloudPath.path.lowercased()).fetchOne(db) + try resolveMetadata(for: cloudPath, database: db) } } @@ -135,13 +143,21 @@ class ItemMetadataDBManager: ItemMetadataManager { func getAllCachedMetadata(inside parent: ItemMetadata) throws -> [ItemMetadata] { precondition(parent.type == .folder) return try database.read { db in - let request: QueryInterfaceRequest - if parent.id == NSFileProviderItemIdentifier.rootContainerDatabaseValue { - request = ItemMetadata.filter(ItemMetadata.Columns.id != NSFileProviderItemIdentifier.rootContainerDatabaseValue) - } else { - request = ItemMetadata.filter(ItemMetadata.Columns.cloudPath.like("\(parent.cloudPath.path + "/")_%")) + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if parent.id == rootID { + return try ItemMetadata.filter(ItemMetadata.Columns.id != rootID).fetchAll(db) } - return try request.fetchAll(db) + return try ItemMetadata.fetchAll(db, sql: """ + WITH RECURSIVE descendants(id, depth) AS ( + SELECT id, 0 FROM itemMetadata WHERE parentID = ? AND id != parentID + UNION ALL + SELECT m.id, d.depth + 1 + FROM itemMetadata m + JOIN descendants d ON m.parentID = d.id + WHERE d.depth < 1024 + ) + SELECT * FROM itemMetadata WHERE id IN (SELECT id FROM descendants) + """, arguments: [parent.id!]) } } @@ -179,13 +195,70 @@ class ItemMetadataDBManager: ItemMetadataManager { return try ItemMetadata.fetchOne(database, key: id) } + private func childOfFolder(parentID: Int64, name: String, database: Database) throws -> ItemMetadata? { + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + let lowercasedName = name.lowercased() + let siblings = try ItemMetadata + .filter(ItemMetadata.Columns.parentID == parentID && ItemMetadata.Columns.id != rootID) + .fetchAll(database) + return siblings.first { $0.name.lowercased() == lowercasedName } + } + + private func resolveMetadata(for cloudPath: CloudPath, database db: Database) throws -> ItemMetadata? { + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if cloudPath == CloudPath("/") { + return try ItemMetadata.fetchOne(db, key: rootID) + } + let components = cloudPath.pathComponents.dropFirst() + var currentID = rootID + var current: ItemMetadata? + for component in components { + guard let child = try childOfFolder(parentID: currentID, name: String(component), database: db) else { + return nil + } + currentID = child.id! + current = child + } + return current + } + + private func resolveCloudPath(for id: Int64, database db: Database) throws -> CloudPath { + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if id == rootID { + return CloudPath("/") + } + let rows = try Row.fetchAll(db, sql: """ + WITH RECURSIVE ancestors(id, parentID, name, depth) AS ( + SELECT id, parentID, name, 0 FROM itemMetadata WHERE id = ? + UNION ALL + SELECT m.id, m.parentID, m.name, a.depth + 1 + FROM itemMetadata m + JOIN ancestors a ON m.id = a.parentID + WHERE a.id != 1 AND a.depth < 1024 + ) + SELECT id, name, depth FROM ancestors ORDER BY depth DESC + """, arguments: [id]) + guard !rows.isEmpty else { + throw FileProviderAdapterError.itemNotFound + } + // Chain must terminate at root; otherwise it's a cycle or orphan. + let topRow = rows.first! + let topID: Int64 = topRow["id"] + if topID != rootID { + throw FileProviderAdapterError.unresolvableParentChain + } + let names: [String] = rows.dropFirst().map { $0["name"] } + return names.reduce(CloudPath("/")) { $0.appendingPathComponent($1) } + } + private func cacheMetadata(_ metadata: ItemMetadata, database: Database) throws { - if let cachedMetadata = try ItemMetadata.fetchOne(database, key: ["cloudPath": metadata.cloudPath]) { - metadata.id = cachedMetadata.id - metadata.statusCode = cachedMetadata.statusCode - metadata.tagData = cachedMetadata.tagData - metadata.favoriteRank = cachedMetadata.favoriteRank - metadata.lastEnumeratedAt = cachedMetadata.lastEnumeratedAt + let cached = try childOfFolder(parentID: metadata.parentID, name: metadata.name, database: database) + if let cached = cached { + metadata.id = cached.id + metadata.statusCode = cached.statusCode + metadata.tagData = cached.tagData + metadata.favoriteRank = cached.favoriteRank + metadata.lastEnumeratedAt = cached.lastEnumeratedAt try metadata.update(database) } else { try metadata.insert(database) diff --git a/CryptomatorFileProvider/DB/ReparentTaskDBManager.swift b/CryptomatorFileProvider/DB/ReparentTaskDBManager.swift index b71058cf4..ab656c2e0 100644 --- a/CryptomatorFileProvider/DB/ReparentTaskDBManager.swift +++ b/CryptomatorFileProvider/DB/ReparentTaskDBManager.swift @@ -11,7 +11,7 @@ import Foundation import GRDB protocol ReparentTaskManager { - func createTaskRecord(for itemMetadata: ItemMetadata, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord + func createTaskRecord(for itemMetadata: ItemMetadata, sourceCloudPath: CloudPath, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord func removeTaskRecord(_ task: ReparentTaskRecord) throws func getTaskRecordsForItemsWhichWere(in parentID: Int64) throws -> [ReparentTaskRecord] func getTaskRecordsForItemsWhichAreSoon(in parentID: Int64) throws -> [ReparentTaskRecord] @@ -28,12 +28,12 @@ class ReparentTaskDBManager: ReparentTaskManager { } } - func createTaskRecord(for itemMetadata: ItemMetadata, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord { + func createTaskRecord(for itemMetadata: ItemMetadata, sourceCloudPath: CloudPath, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord { guard let id = itemMetadata.id else { throw DBManagerError.nonSavedItemMetadata } return try database.write { db in - let task = ReparentTaskRecord(correspondingItem: id, sourceCloudPath: itemMetadata.cloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: newParentID) + let task = ReparentTaskRecord(correspondingItem: id, sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: newParentID) try task.save(db) return task } @@ -66,7 +66,7 @@ class ReparentTaskDBManager: ReparentTaskManager { guard let itemMetadata = try taskRecord.itemMetadata.fetchOne(db) else { throw DBManagerError.missingItemMetadata } - return ReparentTask(taskRecord: taskRecord, itemMetadata: itemMetadata) + return ReparentTask(taskRecord: taskRecord, itemMetadata: itemMetadata, cloudPath: taskRecord.sourceCloudPath) } } } diff --git a/CryptomatorFileProvider/DB/UploadTaskDBManager.swift b/CryptomatorFileProvider/DB/UploadTaskDBManager.swift index ab104ca88..ad20c8783 100644 --- a/CryptomatorFileProvider/DB/UploadTaskDBManager.swift +++ b/CryptomatorFileProvider/DB/UploadTaskDBManager.swift @@ -58,9 +58,11 @@ extension UploadTaskManager { class UploadTaskDBManager: UploadTaskManager { private let database: DatabaseWriter + private let itemMetadataManager: ItemMetadataManager - init(database: DatabaseWriter) { + init(database: DatabaseWriter, itemMetadataManager: ItemMetadataManager) { self.database = database + self.itemMetadataManager = itemMetadataManager } func createNewTaskRecord(for itemMetadata: ItemMetadata) throws -> UploadTaskRecord { @@ -114,12 +116,17 @@ class UploadTaskDBManager: UploadTaskManager { } func getTask(for uploadTask: UploadTaskRecord, onURLSessionTaskCreation: URLSessionTaskCreationClosure?) throws -> UploadTask { - return try database.read { db in - guard let itemMetadata = try uploadTask.itemMetadata.fetchOne(db) else { + let itemMetadata: ItemMetadata = try database.read { db in + guard let metadata = try uploadTask.itemMetadata.fetchOne(db) else { throw DBManagerError.missingItemMetadata } - return UploadTask(taskRecord: uploadTask, itemMetadata: itemMetadata, onURLSessionTaskCreation: onURLSessionTaskCreation) + return metadata + } + guard let id = itemMetadata.id else { + throw DBManagerError.nonSavedItemMetadata } + let cloudPath = try itemMetadataManager.getCloudPath(for: id) + return UploadTask(taskRecord: uploadTask, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: onURLSessionTaskCreation) } func getActiveUploadTaskRecords() throws -> [UploadTaskRecord] { diff --git a/CryptomatorFileProvider/FileProviderAdapter.swift b/CryptomatorFileProvider/FileProviderAdapter.swift index 3bf92e0b9..bce269ec5 100644 --- a/CryptomatorFileProvider/FileProviderAdapter.swift +++ b/CryptomatorFileProvider/FileProviderAdapter.swift @@ -478,7 +478,14 @@ public class FileProviderAdapter: FileProviderAdapterType { return completionHandler(nil, error) } - let task = FolderCreationTask(itemMetadata: placeholderItem.metadata) + let task: FolderCreationTask + do { + let cloudPath = try itemMetadataManager.getCloudPath(for: placeholderItem.metadata.id!) + task = FolderCreationTask(itemMetadata: placeholderItem.metadata, cloudPath: cloudPath) + } catch { + DDLogError("Create directory: getCloudPath failed with error: \(error)") + return completionHandler(nil, error) + } workflowFactory.createWorkflow(for: task).then { workflow -> Workflow in completionHandler(placeholderItem, nil) return workflow @@ -568,12 +575,12 @@ public class FileProviderAdapter: FileProviderAdapterType { name = itemMetadata.name } - let cloudPath = try getCloudPathForPlaceholderItem(withName: name, in: parentID, type: itemMetadata.type) - try checkLocalItemCollision(for: cloudPath) - let taskRecord = try reparentTaskManager.createTaskRecord(for: itemMetadata, targetCloudPath: cloudPath, newParentID: parentID) + let sourceCloudPath = try itemMetadataManager.getCloudPath(for: itemMetadata.id!) + let targetCloudPath = try getCloudPathForPlaceholderItem(withName: name, in: parentID, type: itemMetadata.type) + try checkLocalItemCollision(for: targetCloudPath) + let taskRecord = try reparentTaskManager.createTaskRecord(for: itemMetadata, sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, newParentID: parentID) itemMetadata.name = name - itemMetadata.cloudPath = cloudPath itemMetadata.parentID = parentID itemMetadata.statusCode = .isUploading try itemMetadataManager.updateMetadata(itemMetadata) @@ -636,9 +643,11 @@ public class FileProviderAdapter: FileProviderAdapterType { */ func deleteItemLocally(withIdentifier itemIdentifier: NSFileProviderItemIdentifier) throws -> DeletionTaskRecord { let itemMetadata = try getCachedMetadata(for: itemIdentifier) + // Resolve the cloud path BEFORE removeItemFromCache deletes the row — afterwards the resolver would fail. + let cloudPath = try itemMetadataManager.getCloudPath(for: itemMetadata.id!) let deletionHelper = DeleteItemHelper(itemMetadataManager: itemMetadataManager, cachedFileManager: cachedFileManager) try deletionHelper.removeItemFromCache(itemMetadata) - return try deletionTaskManager.createTaskRecord(for: itemMetadata) + return try deletionTaskManager.createTaskRecord(for: itemMetadata, cloudPath: cloudPath) } // MARK: Start Providing Item @@ -849,7 +858,7 @@ public class FileProviderAdapter: FileProviderAdapterType { let name = localURL.lastPathComponent.precomposedStringWithCanonicalMapping let cloudPath = try getCloudPathForPlaceholderItem(withName: name, in: parentID, type: .file) try checkLocalItemCollision(for: cloudPath) - let placeholderMetadata = ItemMetadata(name: name, type: .file, size: size, parentID: parentID, lastModifiedDate: lastModifiedDate, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true) + let placeholderMetadata = ItemMetadata(name: name, type: .file, size: size, parentID: parentID, lastModifiedDate: lastModifiedDate, statusCode: .isUploading, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(placeholderMetadata) return placeholderMetadata } @@ -868,7 +877,7 @@ public class FileProviderAdapter: FileProviderAdapterType { let normalizedName = name.precomposedStringWithCanonicalMapping let cloudPath = try getCloudPathForPlaceholderItem(withName: normalizedName, in: parentID, type: .folder) try checkLocalItemCollision(for: cloudPath) - let placeholderMetadata = ItemMetadata(name: normalizedName, type: .folder, size: nil, parentID: parentID, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true) + let placeholderMetadata = ItemMetadata(name: normalizedName, type: .folder, size: nil, parentID: parentID, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(placeholderMetadata) return FileProviderItem(metadata: placeholderMetadata, domainIdentifier: domainIdentifier, newestVersionLocallyCached: true) } @@ -882,7 +891,7 @@ public class FileProviderAdapter: FileProviderAdapterType { guard let parentItemMetadata = try itemMetadataManager.getCachedMetadata(for: parentID), parentItemMetadata.type == .folder else { throw FileProviderAdapterError.parentFolderNotFound } - let parentCloudPath = parentItemMetadata.cloudPath + let parentCloudPath = try itemMetadataManager.getCloudPath(for: parentID) return parentCloudPath.appendingPathComponent(name) } @@ -954,17 +963,16 @@ public class FileProviderAdapter: FileProviderAdapterType { return parentItemIdentifier.then { self.enumerateItemsExtensively(for: $0) - }.then { itemList -> NSFileProviderItemIdentifier in - let items = itemList.items - guard let item = items.first(where: { $0.metadata.cloudPath == cloudPath }) else { + }.then { _ -> NSFileProviderItemIdentifier in + guard let metadata = try self.itemMetadataManager.getCachedMetadata(for: cloudPath) else { throw NSFileProviderError(.noSuchItem) } - return item.itemIdentifier + return NSFileProviderItemIdentifier(domainIdentifier: self.domainIdentifier, itemID: metadata.id!) } } func enumerateItemsExtensively(for identifier: NSFileProviderItemIdentifier, withPageToken pageToken: String? = nil) -> Promise { - return enumerateItems(for: identifier, withPageToken: pageToken).then { itemList -> Promisein + return enumerateItems(for: identifier, withPageToken: pageToken).then { itemList -> Promise in if let pageToken = pageToken { return self.enumerateItemsExtensively(for: identifier, withPageToken: pageToken) } diff --git a/CryptomatorFileProvider/FileProviderAdapterError.swift b/CryptomatorFileProvider/FileProviderAdapterError.swift index a2e23434f..06387f2e0 100644 --- a/CryptomatorFileProvider/FileProviderAdapterError.swift +++ b/CryptomatorFileProvider/FileProviderAdapterError.swift @@ -14,4 +14,6 @@ enum FileProviderAdapterError: Error { case parentFolderNotFound case parentItemTypeMismatch case unsupportedItemType + case itemNotFound + case unresolvableParentChain } diff --git a/CryptomatorFileProvider/FileProviderAdapterManager.swift b/CryptomatorFileProvider/FileProviderAdapterManager.swift index 59461809d..0e970ab29 100644 --- a/CryptomatorFileProvider/FileProviderAdapterManager.swift +++ b/CryptomatorFileProvider/FileProviderAdapterManager.swift @@ -179,11 +179,11 @@ public class FileProviderAdapterManager: FileProviderAdapterProviding { let itemMetadataManager = ItemMetadataDBManager(database: database) let cachedFileManager = CachedFileDBManager(database: database, fileManagerHelper: .init(fileCoordinator: fileCoordinator)) - let uploadTaskManager = UploadTaskDBManager(database: database) + let uploadTaskManager = UploadTaskDBManager(database: database, itemMetadataManager: itemMetadataManager) let reparentTaskManager = try ReparentTaskDBManager(database: database) let deletionTaskManager = try DeletionTaskDBManager(database: database) - let itemEnumerationTaskManager = try ItemEnumerationTaskDBManager(database: database) - let downloadTaskManager = try DownloadTaskDBManager(database: database) + let itemEnumerationTaskManager = try ItemEnumerationTaskDBManager(database: database, itemMetadataManager: itemMetadataManager) + let downloadTaskManager = try DownloadTaskDBManager(database: database, itemMetadataManager: itemMetadataManager) let maintenanceManager = MaintenanceDBManager(database: database) let adapter = FileProviderAdapter(domainIdentifier: domainIdentifier, diff --git a/CryptomatorFileProvider/FileProviderItem.swift b/CryptomatorFileProvider/FileProviderItem.swift index e74124db9..b849bddcc 100644 --- a/CryptomatorFileProvider/FileProviderItem.swift +++ b/CryptomatorFileProvider/FileProviderItem.swift @@ -88,7 +88,7 @@ public class FileProviderItem: NSObject, NSFileProviderItem { case .folder: return kUTTypeFolder as String default: - if let typeIdentifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, metadata.cloudPath.pathExtension as CFString, nil) { + if let typeIdentifier = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (metadata.name as NSString).pathExtension as CFString, nil) { let uti = typeIdentifier.takeRetainedValue() as String // Reject dynamic created UTI and use generic UTI (kUTTypeData) instead. See: https://github.com/cryptomator/ios/issues/67#issuecomment-898371262 if uti.hasPrefix("dyn.") { diff --git a/CryptomatorFileProvider/Middleware/OnlineItemNameCollisionHandler.swift b/CryptomatorFileProvider/Middleware/OnlineItemNameCollisionHandler.swift index e6ab7f710..03e661a03 100644 --- a/CryptomatorFileProvider/Middleware/OnlineItemNameCollisionHandler.swift +++ b/CryptomatorFileProvider/Middleware/OnlineItemNameCollisionHandler.swift @@ -40,15 +40,30 @@ class OnlineItemNameCollisionHandler: WorkflowMiddleware { guard case CloudProviderError.itemAlreadyExists = error else { throw error } - let collisionFreeCloudPath = task.itemMetadata.cloudPath.createCollisionCloudPath() - try self.cloudPathCollisionUpdate(with: collisionFreeCloudPath, itemMetadata: task.itemMetadata) - return nextMiddleware.execute(task: task) + let refreshedTask = try self.cloudPathCollisionUpdate(for: task) + return nextMiddleware.execute(task: refreshedTask) } } - func cloudPathCollisionUpdate(with collisionFreeCloudPath: CloudPath, itemMetadata: ItemMetadata) throws { - itemMetadata.name = collisionFreeCloudPath.lastPathComponent - itemMetadata.cloudPath = collisionFreeCloudPath - try itemMetadataManager.updateMetadata(itemMetadata) + func cloudPathCollisionUpdate(for task: CloudTask) throws -> CloudTask { + let collisionFreeCloudPath = task.cloudPath.createCollisionCloudPath() + task.itemMetadata.name = collisionFreeCloudPath.lastPathComponent + try itemMetadataManager.updateMetadata(task.itemMetadata) + switch task { + case let uploadTask as UploadTask: + return uploadTask.with(cloudPath: collisionFreeCloudPath) + case let downloadTask as DownloadTask: + return downloadTask.with(cloudPath: collisionFreeCloudPath) + case let folderCreationTask as FolderCreationTask: + return folderCreationTask.with(cloudPath: collisionFreeCloudPath) + case let itemEnumerationTask as ItemEnumerationTask: + return itemEnumerationTask.with(cloudPath: collisionFreeCloudPath) + case let deletionTask as DeletionTask: + return deletionTask.with(cloudPath: collisionFreeCloudPath) + case let reparentTask as ReparentTask: + return reparentTask.with(cloudPath: collisionFreeCloudPath, taskRecord: reparentTask.taskRecord) + default: + throw FileProviderAdapterError.unsupportedItemType + } } } diff --git a/CryptomatorFileProvider/Middleware/TaskExecutor/DeletionTaskExecutor.swift b/CryptomatorFileProvider/Middleware/TaskExecutor/DeletionTaskExecutor.swift index c77770efd..573444d52 100644 --- a/CryptomatorFileProvider/Middleware/TaskExecutor/DeletionTaskExecutor.swift +++ b/CryptomatorFileProvider/Middleware/TaskExecutor/DeletionTaskExecutor.swift @@ -22,17 +22,17 @@ class DeletionTaskExecutor: WorkflowMiddleware { func execute(task: CloudTask) -> Promise { let itemMetadata = task.itemMetadata - return deleteItemInCloud(itemMetadata).then { + return deleteItemInCloud(type: itemMetadata.type, at: task.cloudPath).then { try self.itemMetadataManager.removeItemMetadata(with: itemMetadata.id!) } } - private func deleteItemInCloud(_ itemMetadata: ItemMetadata) -> Promise { - switch itemMetadata.type { + private func deleteItemInCloud(type: CloudItemType, at cloudPath: CloudPath) -> Promise { + switch type { case .file: - return provider.deleteFile(at: itemMetadata.cloudPath) + return provider.deleteFile(at: cloudPath) case .folder: - return provider.deleteFolder(at: itemMetadata.cloudPath) + return provider.deleteFolder(at: cloudPath) default: return Promise(FileProviderAdapterError.unsupportedItemType) } diff --git a/CryptomatorFileProvider/Middleware/TaskExecutor/DownloadTaskExecutor.swift b/CryptomatorFileProvider/Middleware/TaskExecutor/DownloadTaskExecutor.swift index 6e1c23446..c75be04f1 100644 --- a/CryptomatorFileProvider/Middleware/TaskExecutor/DownloadTaskExecutor.swift +++ b/CryptomatorFileProvider/Middleware/TaskExecutor/DownloadTaskExecutor.swift @@ -53,9 +53,10 @@ class DownloadTaskExecutor: WorkflowMiddleware { let downloadDestination = taskRecord.replaceExisting ? taskRecord.localURL.createCollisionURL() : taskRecord.localURL var lastModifiedDate: Date? let itemMetadata = task.itemMetadata - return provider.fetchItemMetadata(at: itemMetadata.cloudPath).then { cloudMetadata -> Promise in + let cloudPath = task.cloudPath + return provider.fetchItemMetadata(at: cloudPath).then { cloudMetadata -> Promise in lastModifiedDate = cloudMetadata.lastModifiedDate - return self.provider.downloadFile(from: itemMetadata.cloudPath, to: downloadDestination, onTaskCreation: { task in + return self.provider.downloadFile(from: cloudPath, to: downloadDestination, onTaskCreation: { task in guard let task else { return } diff --git a/CryptomatorFileProvider/Middleware/TaskExecutor/FolderCreationTaskExecutor.swift b/CryptomatorFileProvider/Middleware/TaskExecutor/FolderCreationTaskExecutor.swift index fd2623508..17807da33 100644 --- a/CryptomatorFileProvider/Middleware/TaskExecutor/FolderCreationTaskExecutor.swift +++ b/CryptomatorFileProvider/Middleware/TaskExecutor/FolderCreationTaskExecutor.swift @@ -55,7 +55,7 @@ class FolderCreationTaskExecutor: WorkflowMiddleware { assert(itemMetadata.id != nil) assert(itemMetadata.type == .folder) - return provider.createFolder(at: itemMetadata.cloudPath).then { [domainIdentifier, itemMetadataManager] _ -> FileProviderItem in + return provider.createFolder(at: task.cloudPath).then { [domainIdentifier, itemMetadataManager] _ -> FileProviderItem in itemMetadata.statusCode = .isUploaded itemMetadata.isPlaceholderItem = false try itemMetadataManager.updateMetadata(itemMetadata) diff --git a/CryptomatorFileProvider/Middleware/TaskExecutor/ItemEnumerationTaskExecutor.swift b/CryptomatorFileProvider/Middleware/TaskExecutor/ItemEnumerationTaskExecutor.swift index 706903ae7..c7ebe4741 100644 --- a/CryptomatorFileProvider/Middleware/TaskExecutor/ItemEnumerationTaskExecutor.swift +++ b/CryptomatorFileProvider/Middleware/TaskExecutor/ItemEnumerationTaskExecutor.swift @@ -63,13 +63,14 @@ class ItemEnumerationTaskExecutor: WorkflowMiddleware { return Promise(WorkflowMiddlewareError.incompatibleCloudTask) } let itemMetadata = enumerationTask.itemMetadata + let cloudPath = enumerationTask.cloudPath let promise: Promise switch itemMetadata.type { case .folder: let taskRecord = enumerationTask.taskRecord - promise = fetchItemList(folderMetadata: itemMetadata, pageToken: taskRecord.pageToken) + promise = fetchItemList(folderMetadata: itemMetadata, folderCloudPath: cloudPath, pageToken: taskRecord.pageToken) case .file: - promise = fetchItemMetadata(fileMetadata: itemMetadata) + promise = fetchItemMetadata(fileMetadata: itemMetadata, fileCloudPath: cloudPath) default: DDLogError("Unable to enumerate items on metadata type: \(itemMetadata.type)") promise = Promise(NSFileProviderError(.noSuchItem)) @@ -84,8 +85,8 @@ class ItemEnumerationTaskExecutor: WorkflowMiddleware { return promise } - func fetchItemList(folderMetadata: ItemMetadata, pageToken: String?) -> Promise { - return provider.fetchItemList(forFolderAt: folderMetadata.cloudPath, withPageToken: pageToken).then { itemList -> FileProviderItemList in + func fetchItemList(folderMetadata: ItemMetadata, folderCloudPath: CloudPath, pageToken: String?) -> Promise { + return provider.fetchItemList(forFolderAt: folderCloudPath, withPageToken: pageToken).then { itemList -> FileProviderItemList in if pageToken == nil { try self.itemMetadataManager.flagAllItemsAsMaybeOutdated(withParentID: folderMetadata.id!) } @@ -117,21 +118,21 @@ class ItemEnumerationTaskExecutor: WorkflowMiddleware { guard pageToken == nil else { return Promise(error) } - return self.buildOfflineItemList(folderMetadata: folderMetadata, originalError: error) + return self.buildOfflineItemList(folderMetadata: folderMetadata, folderCloudPath: folderCloudPath, originalError: error) } } - private func buildOfflineItemList(folderMetadata: ItemMetadata, originalError: Error) -> Promise { + private func buildOfflineItemList(folderMetadata: ItemMetadata, folderCloudPath: CloudPath, originalError: Error) -> Promise { do { guard folderMetadata.lastEnumeratedAt != nil else { return Promise(originalError) } let cachedMetadata = try itemMetadataManager.getCachedMetadata(withParentID: folderMetadata.id!) let items = try FileProviderItem.items(from: cachedMetadata, domainIdentifier: domainIdentifier, uploadTaskManager: uploadTaskManager, cachedFileManager: cachedFileManager) - DDLogInfo("Offline fallback: serving \(items.count) cached items for folder \(folderMetadata.cloudPath.path)") + DDLogInfo("Offline fallback: serving \(items.count) cached items for folder \(folderCloudPath.path)") return Promise(FileProviderItemList(items: items, nextPageToken: nil)) } catch { - DDLogError("Offline fallback failed for folder \(folderMetadata.cloudPath.path): \(error)") + DDLogError("Offline fallback failed for folder \(folderCloudPath.path): \(error)") return Promise(error) } } @@ -144,19 +145,19 @@ class ItemEnumerationTaskExecutor: WorkflowMiddleware { func filterOutWaitingReparentTasks(parentID: Int64, for itemMetadata: [ItemMetadata]) throws -> [ItemMetadata] { let runningReparentTasks = try reparentTaskManager.getTaskRecordsForItemsWhichWere(in: parentID) return itemMetadata.filter { element in - !runningReparentTasks.contains { $0.sourceCloudPath == element.cloudPath } + !runningReparentTasks.contains { $0.oldParentID == element.parentID && $0.sourceCloudPath.lastPathComponent == element.name } } } func filterOutWaitingDeletionTasks(parentID: Int64, for itemMetadata: [ItemMetadata]) throws -> [ItemMetadata] { let runningDeletionTasks = try deletionTaskManager.getTaskRecordsForItemsWhichWere(in: parentID) return itemMetadata.filter { element in - !runningDeletionTasks.contains { $0.cloudPath == element.cloudPath } + !runningDeletionTasks.contains { $0.parentID == element.parentID && $0.cloudPath.lastPathComponent == element.name } } } - func fetchItemMetadata(fileMetadata: ItemMetadata) -> Promise { - return provider.fetchItemMetadata(at: fileMetadata.cloudPath).then { cloudItem -> FileProviderItemList in + func fetchItemMetadata(fileMetadata: ItemMetadata, fileCloudPath: CloudPath) -> Promise { + return provider.fetchItemMetadata(at: fileCloudPath).then { cloudItem -> FileProviderItemList in let fileProviderItemMetadata = ItemMetadata(item: cloudItem, withParentID: fileMetadata.parentID) try self.itemMetadataManager.cacheMetadata(fileProviderItemMetadata) assert(fileProviderItemMetadata.id == fileMetadata.id) diff --git a/CryptomatorFileProvider/Middleware/TaskExecutor/UploadTaskExecutor.swift b/CryptomatorFileProvider/Middleware/TaskExecutor/UploadTaskExecutor.swift index 9963721fc..296a5d166 100644 --- a/CryptomatorFileProvider/Middleware/TaskExecutor/UploadTaskExecutor.swift +++ b/CryptomatorFileProvider/Middleware/TaskExecutor/UploadTaskExecutor.swift @@ -79,7 +79,7 @@ class UploadTaskExecutor: WorkflowMiddleware { } progress.becomeCurrent(withPendingUnitCount: 1) let uploadPromise = provider.uploadFile(from: localURL, - to: itemMetadata.cloudPath, + to: task.cloudPath, replaceExisting: !itemMetadata.isPlaceholderItem, onTaskCreation: { task in guard let task else { diff --git a/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift b/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift index cf305a7cd..21cb416ca 100644 --- a/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/CachedFileManagerTests.swift @@ -61,7 +61,7 @@ class CachedFileManagerTests: CacheTestCase { func testRemoveCachedFile() throws { let url = tmpDirURL.appendingPathComponent("foo") - let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath(url.path), isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let data = getRandomData(sizeInBytes: 256) try createTestData(localURL: url, data: data, metadata: metadata) @@ -74,14 +74,14 @@ class CachedFileManagerTests: CacheTestCase { func testRemoveCachedFileForPendingUpload() throws { let url = tmpDirURL.appendingPathComponent("foo") - let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath(url.path), isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let data = getRandomData(sizeInBytes: 256) try createTestData(localURL: url, data: data, metadata: metadata) XCTAssertTrue(FileManager.default.fileExists(atPath: url.path)) // Simulate Pending Upload of file foo - let uploadManager = UploadTaskDBManager(database: inMemoryDB) + let uploadManager = UploadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) _ = try uploadManager.createNewTaskRecord(for: metadata) XCTAssertThrowsError(try manager.removeCachedFile(for: XCTUnwrap(metadata.id))) { error in @@ -97,7 +97,7 @@ class CachedFileManagerTests: CacheTestCase { func testRemoveCachedFileForMissingFile() throws { let url = tmpDirURL.appendingPathComponent("foo") - let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath(url.path), isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let data = getRandomData(sizeInBytes: 256) try createTestData(localURL: url, data: data, metadata: metadata) try FileManager.default.removeItem(at: url) @@ -108,7 +108,7 @@ class CachedFileManagerTests: CacheTestCase { func testRemoveCachedFileForFailingFileRemoval() throws { let url = tmpDirURL.appendingPathComponent("foo") - let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath(url.path), isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let data = getRandomData(sizeInBytes: 256) let cachedFileManagerHelperMock = CachedFileManagerHelperMock() @@ -136,7 +136,7 @@ class CachedFileManagerTests: CacheTestCase { func testGetLocalCacheSizeInBytes() throws { let urls = [tmpDirURL.appendingPathComponent("foo"), tmpDirURL.appendingPathComponent("bar")] - let metadata = urls.enumerated().map { ItemMetadata(id: Int64($0 + 2), name: "Item", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath($1.path), isPlaceholderItem: false) } + let metadata = urls.enumerated().map { idx, url in ItemMetadata(id: Int64(idx + 2), name: url.lastPathComponent, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) } let data = getRandomData(sizeInBytes: 256) for (index, url) in urls.enumerated() { @@ -149,7 +149,7 @@ class CachedFileManagerTests: CacheTestCase { func testGetLocalCacheSizeInBytesFiltersOutPendingUploads() throws { let urls = [tmpDirURL.appendingPathComponent("foo"), tmpDirURL.appendingPathComponent("bar")] - let metadata = urls.enumerated().map { ItemMetadata(id: Int64($0 + 2), name: "Item", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath($1.path), isPlaceholderItem: false) } + let metadata = urls.enumerated().map { idx, url in ItemMetadata(id: Int64(idx + 2), name: url.lastPathComponent, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) } let data = getRandomData(sizeInBytes: 256) for (index, url) in urls.enumerated() { @@ -157,7 +157,7 @@ class CachedFileManagerTests: CacheTestCase { } // Simulate Pending Upload of file foo - let uploadManager = UploadTaskDBManager(database: inMemoryDB) + let uploadManager = UploadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) _ = try uploadManager.createNewTaskRecord(for: metadata[0]) XCTAssertEqual(256, try manager.getLocalCacheSizeInBytes()) @@ -166,7 +166,7 @@ class CachedFileManagerTests: CacheTestCase { func testClearCache() throws { let urls = [tmpDirURL.appendingPathComponent("foo"), tmpDirURL.appendingPathComponent("bar")] - let metadata = urls.enumerated().map { ItemMetadata(id: Int64($0 + 2), name: "Item", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath($1.path), isPlaceholderItem: false) } + let metadata = urls.enumerated().map { idx, url in ItemMetadata(id: Int64(idx + 2), name: url.lastPathComponent, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) } let data = getRandomData(sizeInBytes: 256) for (index, url) in urls.enumerated() { @@ -185,7 +185,7 @@ class CachedFileManagerTests: CacheTestCase { let urls = [tmpDirURL.appendingPathComponent("foo"), tmpDirURL.appendingPathComponent("bar"), tmpDirURL.appendingPathComponent("baz")] - let metadata = urls.enumerated().map { ItemMetadata(id: Int64($0 + 2), name: "Item", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath($1.path), isPlaceholderItem: false) } + let metadata = urls.enumerated().map { idx, url in ItemMetadata(id: Int64(idx + 2), name: url.lastPathComponent, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) } let data = getRandomData(sizeInBytes: 256) let cachedFileManagerHelperMock = CachedFileManagerHelperMock() @@ -218,7 +218,7 @@ class CachedFileManagerTests: CacheTestCase { func testGetLocalCachedFileInfoBatch() throws { let urls = [tmpDirURL.appendingPathComponent("foo"), tmpDirURL.appendingPathComponent("bar")] - let metadata = urls.enumerated().map { ItemMetadata(id: Int64($0 + 2), name: "Item", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath($1.path), isPlaceholderItem: false) } + let metadata = urls.enumerated().map { idx, url in ItemMetadata(id: Int64(idx + 2), name: url.lastPathComponent, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) } let data = getRandomData(sizeInBytes: 256) for (index, url) in urls.enumerated() { diff --git a/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift index c9dfdef85..c55ce90ef 100644 --- a/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/DeletionTaskManagerTests.swift @@ -24,9 +24,9 @@ class DeletionTaskManagerTests: XCTestCase { func testCreateAndGetTaskRecord() throws { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - let createdTask = try manager.createTaskRecord(for: itemMetadata) + let createdTask = try manager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let fetchedTask = try manager.getTaskRecord(for: XCTUnwrap(itemMetadata.id)) XCTAssertEqual(fetchedTask, createdTask) XCTAssertEqual(itemMetadata.id, fetchedTask.correspondingItem) @@ -36,9 +36,9 @@ class DeletionTaskManagerTests: XCTestCase { func testRemoveTaskRecord() throws { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - let createdTask = try manager.createTaskRecord(for: itemMetadata) + let createdTask = try manager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) try manager.removeTaskRecord(createdTask) XCTAssertThrowsError(try manager.getTaskRecord(for: XCTUnwrap(itemMetadata.id))) { error in guard case DBManagerError.taskNotFound = error else { @@ -50,21 +50,21 @@ class DeletionTaskManagerTests: XCTestCase { func testGetTaskRecordsWhichWereIn() throws { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata) + _ = try manager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let folderCloudPath = CloudPath("/Folder") - let folderMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) + let folderMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(folderMetadata) - _ = try manager.createTaskRecord(for: folderMetadata) + _ = try manager.createTaskRecord(for: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let subfolderCloudPath = CloudPath("/Folder/SubFolder/") - let subfolderMetadata = try ItemMetadata(name: "SubFolder", type: .folder, size: nil, parentID: XCTUnwrap(folderMetadata.id), lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: subfolderCloudPath, isPlaceholderItem: false) + let subfolderMetadata = try ItemMetadata(name: "SubFolder", type: .folder, size: nil, parentID: XCTUnwrap(folderMetadata.id), lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(subfolderMetadata) - _ = try manager.createTaskRecord(for: subfolderMetadata) + _ = try manager.createTaskRecord(for: subfolderMetadata, cloudPath: subfolderCloudPath) let fileInsideFolderCloudPath = CloudPath("/Folder/FileInsideFolder") - let fileInsideFolderMetadata = try ItemMetadata(name: "FileInsideFolder", type: .file, size: nil, parentID: XCTUnwrap(folderMetadata.id), lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileInsideFolderCloudPath, isPlaceholderItem: false) + let fileInsideFolderMetadata = try ItemMetadata(name: "FileInsideFolder", type: .file, size: nil, parentID: XCTUnwrap(folderMetadata.id), lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(fileInsideFolderMetadata) - _ = try manager.createTaskRecord(for: fileInsideFolderMetadata) + _ = try manager.createTaskRecord(for: fileInsideFolderMetadata, cloudPath: fileInsideFolderCloudPath) let fetchedTasks = try manager.getTaskRecordsForItemsWhichWere(in: XCTUnwrap(folderMetadata.id)) XCTAssertEqual(2, fetchedTasks.count) XCTAssert(fetchedTasks.contains(where: { $0.correspondingItem == fileInsideFolderMetadata.id && $0.cloudPath == fileInsideFolderCloudPath })) @@ -73,9 +73,9 @@ class DeletionTaskManagerTests: XCTestCase { func testGetTask() throws { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - let taskRecord = try manager.createTaskRecord(for: itemMetadata) + let taskRecord = try manager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let fetchedTask = try manager.getTask(for: taskRecord) XCTAssertEqual(itemMetadata, fetchedTask.itemMetadata) XCTAssertEqual(itemMetadata.id, fetchedTask.taskRecord.correspondingItem) diff --git a/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift index e1fd5935a..7d8db1564 100644 --- a/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/DownloadTaskManagerTests.swift @@ -19,7 +19,7 @@ class DownloadTaskManagerTests: XCTestCase { override func setUpWithError() throws { inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) - manager = try DownloadTaskDBManager(database: inMemoryDB) + manager = try DownloadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) } @@ -32,6 +32,29 @@ class DownloadTaskManagerTests: XCTestCase { XCTAssert(fetchedTask.replaceExisting) } + func testInitWipesPendingDownloadTasksOnly() throws { + // Init must wipe only download tasks, not the enumeration table. + let freshDB = try DatabaseQueue() + try DatabaseHelper.migrate(freshDB) + let metadataManager = ItemMetadataDBManager(database: freshDB) + + let downloadItem = ItemMetadata(name: "DL.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let enumItem = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + try metadataManager.cacheMetadata(downloadItem) + try metadataManager.cacheMetadata(enumItem) + try freshDB.write { db in + try DownloadTaskRecord(correspondingItem: downloadItem.id!, replaceExisting: false, localURL: URL(string: "/tmp/dl")!).save(db) + try ItemEnumerationTaskRecord(correspondingItem: enumItem.id!, pageToken: nil).save(db) + } + + _ = try DownloadTaskDBManager(database: freshDB, itemMetadataManager: metadataManager) + + try freshDB.read { db in + XCTAssertEqual(0, try DownloadTaskRecord.fetchCount(db), "init should wipe pending download tasks") + XCTAssertEqual(1, try ItemEnumerationTaskRecord.fetchCount(db), "init must not touch the enumeration table") + } + } + func testRemoveTaskRecord() throws { let createdTask = try createTestTask() try manager.removeTaskRecord(createdTask.taskRecord) @@ -45,7 +68,7 @@ class DownloadTaskManagerTests: XCTestCase { private func createTestTask() throws -> DownloadTask { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) let localURL = URL(string: "/Test")! return try manager.createTask(for: itemMetadata, replaceExisting: true, localURL: localURL, onURLSessionTaskCreation: nil) diff --git a/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift index 2a73e1cb0..0c378c158 100644 --- a/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/ItemEnumerationTaskManagerTests.swift @@ -19,13 +19,13 @@ class ItemEnumerationTaskManagerTests: XCTestCase { override func setUpWithError() throws { inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) - manager = try ItemEnumerationTaskDBManager(database: inMemoryDB) + manager = try ItemEnumerationTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) } func testCreateAndGetTaskRecord() throws { let cloudPath = CloudPath("/Test") - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) let pageToken: String? = nil let createdTask = try manager.createTask(for: itemMetadata, pageToken: pageToken) diff --git a/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift b/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift index fdcc38c32..3a14202d8 100644 --- a/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/MaintenanceManagerTests.swift @@ -27,11 +27,11 @@ class MaintenanceManagerTests: XCTestCase { try DatabaseHelper.migrate(inMemoryDB) manager = MaintenanceDBManager(database: inMemoryDB) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) - uploadTaskManager = UploadTaskDBManager(database: inMemoryDB) + uploadTaskManager = UploadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) reparentTaskManager = try ReparentTaskDBManager(database: inMemoryDB) deletionTaskManager = try DeletionTaskDBManager(database: inMemoryDB) - itemEnumerationTaskManager = try ItemEnumerationTaskDBManager(database: inMemoryDB) - downloadTaskManager = try DownloadTaskDBManager(database: inMemoryDB) + itemEnumerationTaskManager = try ItemEnumerationTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) + downloadTaskManager = try DownloadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) } func testPreventEnablingMaintenanceModeTwice() throws { @@ -45,7 +45,7 @@ class MaintenanceManagerTests: XCTestCase { // MARK: - Prevent the creation of New tasks when in maintenance mode func testPreventCreatingUploadTasks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) // Prevent INSERT @@ -54,25 +54,25 @@ class MaintenanceManagerTests: XCTestCase { } func testPreventCreatingReparentTasks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) // Prevent INSERT try manager.enableMaintenanceMode() - try checkThrowsMaintenanceError(reparentTaskManager.createTaskRecord(for: itemMetadata, targetCloudPath: CloudPath("Foo"), newParentID: 1)) + try checkThrowsMaintenanceError(reparentTaskManager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: CloudPath("Foo"), newParentID: 1)) } func testPreventCreatingDeletionTasks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) // Prevent INSERT try manager.enableMaintenanceMode() - try checkThrowsMaintenanceError(deletionTaskManager.createTaskRecord(for: itemMetadata)) + try checkThrowsMaintenanceError(deletionTaskManager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name))) } func testPreventCreatingItemEnumerationTasks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) // Prevent INSERT @@ -81,7 +81,7 @@ class MaintenanceManagerTests: XCTestCase { } func testPreventCreatingDownloadTasks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) // Prevent INSERT @@ -92,14 +92,14 @@ class MaintenanceManagerTests: XCTestCase { // MARK: - Prevent enabling maintenance mode for running tasks func testPreventEnablingMaintenanceModeForRunningUploadTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try uploadTaskManager.createNewTaskRecord(for: itemMetadata) try assertOnlyFalseAllowedForInsertOrUpdate() } func testAllowMaintenanceModeForFailedUploadTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) var task = try uploadTaskManager.createNewTaskRecord(for: itemMetadata) @@ -109,28 +109,28 @@ class MaintenanceManagerTests: XCTestCase { } func testPreventEnablingMaintenanceModeForRunningReparentTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try reparentTaskManager.createTaskRecord(for: itemMetadata, targetCloudPath: CloudPath("Foo"), newParentID: 1) + _ = try reparentTaskManager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: CloudPath("Foo"), newParentID: 1) try assertOnlyFalseAllowedForInsertOrUpdate() } func testPreventEnablingMaintenanceModeForRunningDeletionTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try deletionTaskManager.createTaskRecord(for: itemMetadata) + _ = try deletionTaskManager.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) try assertOnlyFalseAllowedForInsertOrUpdate() } func testPreventEnablingMaintenanceModeForRunningItemEnumerationTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try itemEnumerationTaskManager.createTask(for: itemMetadata, pageToken: nil) try assertOnlyFalseAllowedForInsertOrUpdate() } func testPreventEnablingMaintenanceModeForRunningDownloadTask() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try downloadTaskManager.createTask(for: itemMetadata, replaceExisting: false, localURL: XCTUnwrap(URL(string: "/Test")), onURLSessionTaskCreation: nil) try assertOnlyFalseAllowedForInsertOrUpdate() diff --git a/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift b/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift index e4be8e517..e81a5006e 100644 --- a/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/MetadataManagerTests.swift @@ -23,7 +23,7 @@ class MetadataManagerTests: XCTestCase { func testCacheMetadataForFile() throws { let cloudPath = CloudPath("/TestFile") - let itemMetadata = ItemMetadata(name: "TestFile", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "TestFile", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) XCTAssertNil(itemMetadata.id) try manager.cacheMetadata(itemMetadata) XCTAssertNotNil(itemMetadata.id) @@ -38,7 +38,7 @@ class MetadataManagerTests: XCTestCase { func testCacheMetadataForFolder() throws { let cloudPath = CloudPath("/Test Folder/") - let itemMetadata = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) XCTAssertNil(itemMetadata.id) try manager.cacheMetadata(itemMetadata) guard let fetchedMetadata = try manager.getCachedMetadata(for: cloudPath) else { @@ -52,8 +52,8 @@ class MetadataManagerTests: XCTestCase { func testCacheMultipleEntries() throws { let fileCloudPath = CloudPath("/TestFile") let folderCloudPath = CloudPath("/TestFolder/") - let itemMetadataForFile = ItemMetadata(name: "TestFile", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: false) - let itemMetadataForFolder = ItemMetadata(name: "TestFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: true) + let itemMetadataForFile = ItemMetadata(name: "TestFile", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let itemMetadataForFolder = ItemMetadata(name: "TestFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) XCTAssertNil(itemMetadataForFile.id) XCTAssertNil(itemMetadataForFolder.id) let itemMetadataList = [itemMetadataForFile, itemMetadataForFolder] @@ -76,9 +76,9 @@ class MetadataManagerTests: XCTestCase { let fileCloudPath = CloudPath("/Test File.txt") let folderCloudPath = CloudPath("/Test Folder/") let secondFolderCloudPath = CloudPath("/SecondFolder/") - let placeholderItemMetadataForFile = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: true) - let placeholderItemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: true) - let itemMetadataForFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: secondFolderCloudPath, isPlaceholderItem: false) + let placeholderItemMetadataForFile = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) + let placeholderItemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) + let itemMetadataForFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) XCTAssertNil(placeholderItemMetadataForFile.id) XCTAssertNil(placeholderItemMetadataForFolder.id) XCTAssertNil(itemMetadataForFolder.id) @@ -95,8 +95,8 @@ class MetadataManagerTests: XCTestCase { let fileCloudPath = CloudPath("/Test File.txt") let folderCloudPath = CloudPath("/Test Folder/") let secondFolderCloudPath = CloudPath("/Test Folder/SecondFolder/") - let placeholderItemMetadataForFile = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: false) - let placeholderItemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) + let placeholderItemMetadataForFile = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let placeholderItemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) XCTAssertNil(placeholderItemMetadataForFile.id) XCTAssertNil(placeholderItemMetadataForFolder.id) try manager.cacheMetadata([placeholderItemMetadataForFile, placeholderItemMetadataForFolder]) @@ -105,7 +105,7 @@ class MetadataManagerTests: XCTestCase { XCTFail("Test Folder ID is nil") return } - let itemMetadataForFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: testFolderId, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: secondFolderCloudPath, isPlaceholderItem: true) + let itemMetadataForFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: testFolderId, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) XCTAssertNil(itemMetadataForFolder.id) try manager.cacheMetadata(itemMetadataForFolder) XCTAssertNotNil(itemMetadataForFolder.id) @@ -116,7 +116,7 @@ class MetadataManagerTests: XCTestCase { func testOverwriteMetadata() throws { let cloudPath = CloudPath("/TestFolder/") - let itemMetadata = ItemMetadata(name: "TestFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "TestFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadata) let id = try XCTUnwrap(itemMetadata.id) XCTAssertEqual(2, id) @@ -127,12 +127,12 @@ class MetadataManagerTests: XCTestCase { XCTAssertEqual(itemMetadata, fetchedItemMetadata) let fileCloudPath = CloudPath("/Existing File.txt") - let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: false) + let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadataForFile) let secondItemID = try XCTUnwrap(itemMetadataForFile.id) XCTAssertEqual(3, secondItemID) - let changedItemMetadataAtSameRemoteURL = ItemMetadata(name: "TestFolder", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let changedItemMetadataAtSameRemoteURL = ItemMetadata(name: "TestFolder", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(changedItemMetadataAtSameRemoteURL) let changedItemID = try XCTUnwrap(changedItemMetadataAtSameRemoteURL.id) XCTAssertEqual(2, changedItemID) @@ -148,8 +148,8 @@ class MetadataManagerTests: XCTestCase { func testGetCachedMetadataInsideParentId() throws { let fileCloudPath = CloudPath("/Existing File.txt") let folderCloudPath = CloudPath("/Existing Folder/") - let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: false) - let itemMetadataForFolder = ItemMetadata(name: "Existing Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) + let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let itemMetadataForFolder = ItemMetadata(name: "Existing Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) XCTAssertNil(itemMetadataForFile.id) XCTAssertNil(itemMetadataForFolder.id) try manager.cacheMetadata([itemMetadataForFile, itemMetadataForFolder]) @@ -165,12 +165,12 @@ class MetadataManagerTests: XCTestCase { func testFlagAllNonPlaceholderItemsAsCacheCleanupCandidates() throws { let placeholderFileCloudPath = CloudPath("/Placeholder File.txt") let placeholderFolderCloudPath = CloudPath("/Placeholder Folder/") - let placeholderItemMetadataForFile = ItemMetadata(name: "Placeholder File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: placeholderFileCloudPath, isPlaceholderItem: true) - let placeholderItemMetadataForFolder = ItemMetadata(name: "Placeholder Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: placeholderFolderCloudPath, isPlaceholderItem: true) + let placeholderItemMetadataForFile = ItemMetadata(name: "Placeholder File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) + let placeholderItemMetadataForFolder = ItemMetadata(name: "Placeholder Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) let fileCloudPath = CloudPath("/Existing File.txt") let folderCloudPath = CloudPath("/Existing Folder/") - let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileCloudPath, isPlaceholderItem: false) - let itemMetadataForFolder = ItemMetadata(name: "Existing Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) + let itemMetadataForFile = ItemMetadata(name: "Existing File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let itemMetadataForFolder = ItemMetadata(name: "Existing Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata([placeholderItemMetadataForFile, placeholderItemMetadataForFolder, itemMetadataForFile, itemMetadataForFolder]) try manager.flagAllItemsAsMaybeOutdated(withParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) let cachedMetadata = try manager.getCachedMetadata(withParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) @@ -188,21 +188,21 @@ class MetadataManagerTests: XCTestCase { let secondFolderCloudPath = CloudPath("/Test Folder 1/") let subFolderCloudPath = CloudPath("/Test Folder/SecondFolder/") - let itemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) - let secondItemMetadataForFolder = ItemMetadata(name: "Test Folder 1", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: secondFolderCloudPath, isPlaceholderItem: false) + let itemMetadataForFolder = ItemMetadata(name: "Test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let secondItemMetadataForFolder = ItemMetadata(name: "Test Folder 1", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata([itemMetadataForFolder, secondItemMetadataForFolder]) guard let folderId = itemMetadataForFolder.id else { XCTFail("Folder has no ID") return } - let itemMetadataForFileInFolder = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: folderId, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileInFolderCloudPath, isPlaceholderItem: false) - let itemMetadataForSubFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: folderId, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: subFolderCloudPath, isPlaceholderItem: false) + let itemMetadataForFileInFolder = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: folderId, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let itemMetadataForSubFolder = ItemMetadata(name: "SecondFolder", type: .folder, size: nil, parentID: folderId, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadataForSubFolder) guard let subFolderId = itemMetadataForSubFolder.id else { XCTFail("Folder has no ID") return } - let itemMetadataForFileInSubFolder = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: subFolderId, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: fileInSubFolderCloudPath, isPlaceholderItem: false) + let itemMetadataForFileInSubFolder = ItemMetadata(name: "Test File.txt", type: .file, size: 100, parentID: subFolderId, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata([itemMetadataForFileInFolder, itemMetadataForFileInSubFolder]) let cachedMetadata = try manager.getAllCachedMetadata(inside: itemMetadataForFolder) @@ -214,7 +214,7 @@ class MetadataManagerTests: XCTestCase { func testGetMetadataWithCaseMismatchPath() throws { let cloudPath = CloudPath("/File.txt") - let itemMetadataForFile = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadataForFile = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadataForFile) guard let fetchedMetadataForSensitivePath = try manager.getCachedMetadata(for: cloudPath) else { @@ -227,14 +227,14 @@ class MetadataManagerTests: XCTestCase { return } XCTAssertEqual(fetchedMetadataForSensitivePath, fetchedMetadataForInSensitivePath) - XCTAssertEqual(cloudPath, fetchedMetadataForInSensitivePath.cloudPath) + XCTAssertEqual("File.txt", fetchedMetadataForInSensitivePath.name) } // MARK: Set Tag Data func testSetTagData() throws { let cloudPath = CloudPath("/File.txt") - let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadata) let tagData = Data("Foo".utf8) let id = try XCTUnwrap(itemMetadata.id) @@ -247,7 +247,7 @@ class MetadataManagerTests: XCTestCase { func testSetTagDataToNil() throws { let cloudPath = CloudPath("/File.txt") let tagData = Data("Foo".utf8) - let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false, tagData: tagData) + let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, tagData: tagData) try manager.cacheMetadata(itemMetadata) let id = try XCTUnwrap(itemMetadata.id) @@ -260,7 +260,7 @@ class MetadataManagerTests: XCTestCase { func testCacheMetadataDoesNotOverwriteExistingTagData() throws { let cloudPath = CloudPath("/File.txt") let tagData = Data("Foo".utf8) - let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false, tagData: tagData) + let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, tagData: tagData) try manager.cacheMetadata(itemMetadata) itemMetadata.tagData = nil @@ -275,7 +275,7 @@ class MetadataManagerTests: XCTestCase { func testSetFavoriteRank() throws { let cloudPath = CloudPath("/Folder") - let itemMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try manager.cacheMetadata(itemMetadata) let favoriteRank: Int64 = 100 let id = try XCTUnwrap(itemMetadata.id) @@ -287,7 +287,7 @@ class MetadataManagerTests: XCTestCase { func testSetFavoriteRankToNil() throws { let cloudPath = CloudPath("/Folder") - let itemMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false, favoriteRank: 100) + let itemMetadata = ItemMetadata(name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, favoriteRank: 100) try manager.cacheMetadata(itemMetadata) let id = try XCTUnwrap(itemMetadata.id) try manager.setFavoriteRank(to: nil, forItemWithID: id) @@ -299,7 +299,7 @@ class MetadataManagerTests: XCTestCase { func testCacheMetadataDoesNotOverwriteExistingFavoriteRank() throws { let cloudPath = CloudPath("/Folder") let favoriteRank: Int64 = 100 - let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false, favoriteRank: favoriteRank) + let itemMetadata = ItemMetadata(name: "File.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, favoriteRank: favoriteRank) try manager.cacheMetadata(itemMetadata) itemMetadata.favoriteRank = nil @@ -315,8 +315,4 @@ extension ItemMetadata: Comparable { public static func < (lhs: ItemMetadata, rhs: ItemMetadata) -> Bool { return lhs.name < rhs.name } - - public static func == (lhs: ItemMetadata, rhs: ItemMetadata) -> Bool { - return lhs.name == rhs.name && lhs.type == rhs.type && lhs.size == rhs.size && lhs.parentID == rhs.parentID && lhs.lastModifiedDate == rhs.lastModifiedDate && lhs.statusCode == rhs.statusCode && lhs.cloudPath == rhs.cloudPath && lhs.isPlaceholderItem == rhs.isPlaceholderItem && lhs.isMaybeOutdated == rhs.isMaybeOutdated - } } diff --git a/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift index 78cbd2fce..7d7bda50f 100644 --- a/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/ReparentTaskManagerTests.swift @@ -27,10 +27,10 @@ class ReparentTaskManagerTests: XCTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Foo.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) let newParentID: Int64 = 3 - let createdTask = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: newParentID) + let createdTask = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: newParentID) let fetchedTask = try getTaskRecord(for: itemID) XCTAssertEqual(createdTask, fetchedTask) XCTAssertEqual(itemID, fetchedTask.correspondingItem) @@ -44,9 +44,9 @@ class ReparentTaskManagerTests: XCTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Foo.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: 3) + _ = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: 3) let task = try getTaskRecord(for: itemID) try manager.removeTaskRecord(task) XCTAssertThrowsError(try getTaskRecord(for: itemID)) { error in @@ -62,14 +62,14 @@ class ReparentTaskManagerTests: XCTestCase { let targetCloudPath = CloudPath("/Foo/Bar.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) let folderItemID: Int64 = 3 - let folderItemMetadata = ItemMetadata(id: folderItemID, name: "Foo", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Foo"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let folderItemMetadata = ItemMetadata(id: folderItemID, name: "Foo", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(folderItemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: folderItemID) + _ = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: folderItemID) let retrievedTasks = try manager.getTaskRecordsForItemsWhichWere(in: NSFileProviderItemIdentifier.rootContainerDatabaseValue) XCTAssertEqual(1, retrievedTasks.count) @@ -85,10 +85,10 @@ class ReparentTaskManagerTests: XCTestCase { let targetCloudPath = CloudPath("/Test2 - Only Renamed.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) + _ = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) let retrievedTasks = try manager.getTaskRecordsForItemsWhichWere(in: NSFileProviderItemIdentifier.rootContainerDatabaseValue) XCTAssertEqual(1, retrievedTasks.count) XCTAssertEqual(itemID, retrievedTasks[0].correspondingItem) @@ -104,10 +104,10 @@ class ReparentTaskManagerTests: XCTestCase { let newParentID: Int64 = 3 let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: newParentID) + _ = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: newParentID) let retrievedTasks = try manager.getTaskRecordsForItemsWhichAreSoon(in: newParentID) XCTAssertEqual(1, retrievedTasks.count) @@ -123,10 +123,10 @@ class ReparentTaskManagerTests: XCTestCase { let targetCloudPath = CloudPath("/Test2 - Only Renamed.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try itemMetadataManager.cacheMetadata(itemMetadata) - _ = try manager.createTaskRecord(for: itemMetadata, targetCloudPath: targetCloudPath, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) + _ = try manager.createTaskRecord(for: itemMetadata, sourceCloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name), targetCloudPath: targetCloudPath, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) let retrievedTasks = try manager.getTaskRecordsForItemsWhichAreSoon(in: NSFileProviderItemIdentifier.rootContainerDatabaseValue) XCTAssertEqual(1, retrievedTasks.count) diff --git a/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift b/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift index 2e6e20cc0..a69f4e7c9 100644 --- a/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift +++ b/CryptomatorFileProviderTests/DB/UploadTaskManagerTests.swift @@ -20,12 +20,12 @@ class UploadTaskManagerTests: XCTestCase { override func setUpWithError() throws { inMemoryDB = try DatabaseQueue() try DatabaseHelper.migrate(inMemoryDB) - manager = UploadTaskDBManager(database: inMemoryDB) + manager = UploadTaskDBManager(database: inMemoryDB, itemMetadataManager: ItemMetadataDBManager(database: inMemoryDB)) itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) } func testCachedAndFetchEntry() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try manager.createNewTaskRecord(for: itemMetadata) guard let fetchedUploadTask = try manager.getTaskRecord(for: XCTUnwrap(itemMetadata.id)) else { @@ -39,7 +39,7 @@ class UploadTaskManagerTests: XCTestCase { } func testUpdateTaskRecord() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try manager.createNewTaskRecord(for: itemMetadata) let lastFailedUploadDate = Date(timeIntervalSinceReferenceDate: 0) @@ -65,7 +65,7 @@ class UploadTaskManagerTests: XCTestCase { } func testDeleteCascadeWorks() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try manager.createNewTaskRecord(for: itemMetadata) let taskBeforeRemoval = try manager.getTaskRecord(for: XCTUnwrap(itemMetadata.id)) @@ -77,7 +77,7 @@ class UploadTaskManagerTests: XCTestCase { } func testGetCorrespondingTaskRecords() throws { - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) let savedTask = try manager.createNewTaskRecord(for: itemMetadata) let ids = try [XCTUnwrap(itemMetadata.id) + 1, XCTUnwrap(itemMetadata.id), XCTUnwrap(itemMetadata.id) - 1] @@ -91,7 +91,7 @@ class UploadTaskManagerTests: XCTestCase { func testGetTask() throws { let cloudPath = CloudPath("/Test") let itemMetadataManager = ItemMetadataDBManager(database: inMemoryDB) - let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) let taskRecord = try manager.createNewTaskRecord(for: itemMetadata) let fetchedTask = try manager.getTask(for: taskRecord, onURLSessionTaskCreation: nil) @@ -115,7 +115,7 @@ class UploadTaskManagerTests: XCTestCase { } func testGetRetryableUploadTaskRecordsExcludesNilErrorTasks() throws { - let itemMetadata = ItemMetadata(name: "ActiveUpload", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: CloudPath("/ActiveUpload"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: "ActiveUpload", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try manager.createNewTaskRecord(for: itemMetadata) let retryable = try manager.getRetryableUploadTaskRecords() @@ -127,7 +127,7 @@ class UploadTaskManagerTests: XCTestCase { @discardableResult private func createTaskRecordWithError(domain: String, code: Int, name: String? = nil) throws -> UploadTaskRecord { let fileName = name ?? "File-\(domain)-\(code)" - let itemMetadata = ItemMetadata(name: fileName, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: CloudPath("/\(fileName)"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(name: fileName, type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) try itemMetadataManager.cacheMetadata(itemMetadata) _ = try manager.createNewTaskRecord(for: itemMetadata) let id = try XCTUnwrap(itemMetadata.id) diff --git a/CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift b/CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift index 63e10857b..50478c504 100644 --- a/CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift +++ b/CryptomatorFileProviderTests/FileImportingServiceSourceTests.swift @@ -22,7 +22,7 @@ class FileImportingServiceSourceTests: XCTestCase { var taskRegistratorMock: SessionTaskRegistratorMock! let dbPath = FileManager.default.temporaryDirectory let domain = NSFileProviderDomain(identifier: .test, displayName: "Foo", pathRelativeToDocumentStorage: "/") - let itemStub = FileProviderItem(metadata: .init(name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: CloudPath("/foo"), isPlaceholderItem: false), domainIdentifier: .test) + let itemStub = FileProviderItem(metadata: .init(name: "Foo", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false), domainIdentifier: .test) override func setUpWithError() throws { notificatorMock = FileProviderNotificatorTypeMock() diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterCreateDirectoryTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterCreateDirectoryTests.swift index 11f975d17..50ca1730f 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterCreateDirectoryTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterCreateDirectoryTests.swift @@ -13,7 +13,7 @@ import XCTest class FileProviderAdapterCreateDirectoryTests: FileProviderAdapterTestCase { func testCreateDirectory() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let adapter = FileProviderAdapter(domainIdentifier: .test, uploadTaskManager: uploadTaskManagerMock, cachedFileManager: cachedFileManagerMock, itemMetadataManager: metadataManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, downloadTaskManager: downloadTaskManagerMock, scheduler: WorkflowSchedulerMock(), provider: cloudProviderMock, coordinator: fileCoordinator, localURLProvider: localURLProviderMock, taskRegistrator: taskRegistratorMock) adapter.createDirectory(withName: "TestFolder", inParentItemIdentifier: .rootContainer) { item, error in @@ -27,7 +27,7 @@ class FileProviderAdapterCreateDirectoryTests: FileProviderAdapterTestCase { XCTAssertFalse(fileProviderItem.isUploaded) XCTAssertEqual("public.folder", fileProviderItem.typeIdentifier) XCTAssert(fileProviderItem.metadata.isPlaceholderItem) - XCTAssertEqual(CloudPath("/TestFolder"), fileProviderItem.metadata.cloudPath) + XCTAssertEqual("TestFolder", fileProviderItem.metadata.name) XCTAssertNotNil(fileProviderItem.metadata.id) XCTAssertEqual(2, self.metadataManagerMock.cachedMetadata.count) @@ -62,7 +62,7 @@ class FileProviderAdapterCreateDirectoryTests: FileProviderAdapterTestCase { // MARK: Create Placeholder func testCreatePlaceholderItemForFolder() throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let placeholderItem = try adapter.createPlaceholderItemForFolder(withName: "TestFolder", in: .rootContainer) @@ -71,7 +71,7 @@ class FileProviderAdapterCreateDirectoryTests: FileProviderAdapterTestCase { XCTAssertFalse(placeholderItem.isUploaded) XCTAssertEqual("public.folder", placeholderItem.typeIdentifier) XCTAssert(placeholderItem.metadata.isPlaceholderItem) - XCTAssertEqual(CloudPath("/TestFolder"), placeholderItem.metadata.cloudPath) + XCTAssertEqual("TestFolder", placeholderItem.metadata.name) XCTAssertNotNil(placeholderItem.metadata.id) XCTAssertEqual(2, metadataManagerMock.cachedMetadata.count) diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterDeleteItemTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterDeleteItemTests.swift index 29e3911b6..0b8f6c540 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterDeleteItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterDeleteItemTests.swift @@ -15,7 +15,7 @@ class FileProviderAdapterDeleteItemTests: FileProviderAdapterTestCase { let expectation = XCTestExpectation() let itemID: Int64 = 2 let cloudPath = CloudPath("/test.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let adapter = createFullyMockedAdapter() let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) @@ -39,9 +39,9 @@ class FileProviderAdapterDeleteItemTests: FileProviderAdapterTestCase { let folderItemID: Int64 = 2 let fileItemID: Int64 = 3 let folderCloudPath = CloudPath("/Folder/") - let folderItemMetadata = ItemMetadata(id: folderItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: folderCloudPath, isPlaceholderItem: false) + let folderItemMetadata = ItemMetadata(id: folderItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let cloudPath = CloudPath("Folder/test.txt") - let fileItemMetadata = ItemMetadata(id: fileItemID, name: "test.txt", type: .file, size: nil, parentID: folderItemID, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let fileItemMetadata = ItemMetadata(id: fileItemID, name: "test.txt", type: .file, size: nil, parentID: folderItemID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata([folderItemMetadata, fileItemMetadata]) let folderItemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: folderItemID) @@ -69,7 +69,7 @@ class FileProviderAdapterDeleteItemTests: FileProviderAdapterTestCase { let expectation = XCTestExpectation() let itemID: Int64 = 2 let cloudPath = CloudPath("/test.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let adapter = createFullyMockedAdapter() let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift index 30c97c11e..05bece288 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterEnumerateItemTests.swift @@ -22,18 +22,20 @@ class FileProviderAdapterEnumerateItemTests: FileProviderAdapterTestCase { // MARK: Error Handling func testEnumerateItemsFailedWithNoInternetConnection() throws { - let metadata = ItemMetadata(id: 2, name: "noInternetConnection", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/noInternetConnection"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data()) + let metadata = ItemMetadata(id: 2, name: "noInternetConnection", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data()) try metadataManagerMock.cacheMetadata(metadata) + itemEnumerationTaskManagerMock.stubCloudPath = CloudPath("/noInternetConnection") XCTAssertRejects(adapter.enumerateItems(for: NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 2), withPageToken: nil), with: NSFileProviderError(.serverUnreachable)) } func testEnumerateItemsOfflineFallbackToCache() throws { let expectation = XCTestExpectation() - let metadata = ItemMetadata(id: 2, name: "noInternetConnection", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/noInternetConnection"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data(), lastEnumeratedAt: Date()) + let metadata = ItemMetadata(id: 2, name: "noInternetConnection", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data(), lastEnumeratedAt: Date()) try metadataManagerMock.cacheMetadata(metadata) - let child = ItemMetadata(id: 3, name: "CachedFile", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/noInternetConnection/CachedFile"), isPlaceholderItem: false) + let child = ItemMetadata(id: 3, name: "CachedFile", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(child) + itemEnumerationTaskManagerMock.stubCloudPath = CloudPath("/noInternetConnection") let permissionProviderMock = PermissionProviderMock() withDependencies { @@ -58,8 +60,8 @@ class FileProviderAdapterEnumerateItemTests: FileProviderAdapterTestCase { func testWorkingSet() { let mockMetadata = [ - ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data()), - ItemMetadata(id: 3, name: "TestFolder", type: .file, size: nil, parentID: 4, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Foo/TestFolder"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: 1, tagData: nil) + ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: Data()), + ItemMetadata(id: 3, name: "TestFolder", type: .file, size: nil, parentID: 4, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: 1, tagData: nil) ] metadataManagerMock.workingSetMetadata = mockMetadata let permissionProviderMock = PermissionProviderMock() diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemIdentifierTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemIdentifierTests.swift index 910616b6c..d62b580a5 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemIdentifierTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemIdentifierTests.swift @@ -24,7 +24,6 @@ class FileProviderAdapterGetItemIdentifierTests: FileProviderAdapterTestCase { parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, - cloudPath: CloudPath("/"), isPlaceholderItem: false) } @@ -45,7 +44,6 @@ class FileProviderAdapterGetItemIdentifierTests: FileProviderAdapterTestCase { parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, - cloudPath: CloudPath("/Directory 1"), isPlaceholderItem: false) let folderMetadataID = try XCTUnwrap(folderMetadata.id) @@ -58,7 +56,6 @@ class FileProviderAdapterGetItemIdentifierTests: FileProviderAdapterTestCase { parentID: XCTUnwrap(folderMetadata.id), lastModifiedDate: nil, statusCode: .isUploaded, - cloudPath: CloudPath("/Directory 1/Directory 2"), isPlaceholderItem: false) let subFolderMetadataID = try XCTUnwrap(subFolderMetadata.id) metadataManagerMock.cachedMetadata[subFolderMetadataID] = subFolderMetadata @@ -70,43 +67,20 @@ class FileProviderAdapterGetItemIdentifierTests: FileProviderAdapterTestCase { XCTAssertEqual(expectedItemIdentifier, itemIdentifier) } - func testGetItemIdentifierForCachedItemMissingInCloud() throws { - let cloudPath = CloudPath("/Directory 1/Directory 3") - let folderMetadata = ItemMetadata(id: 2, - name: "Directory 1", - type: .folder, - size: nil, - parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, - lastModifiedDate: nil, - statusCode: .isUploaded, - cloudPath: CloudPath("/Directory 1"), - isPlaceholderItem: false) - - let folderMetadataID = try XCTUnwrap(folderMetadata.id) - metadataManagerMock.cachedMetadata[folderMetadataID] = folderMetadata - - let subFolderMetadata = try ItemMetadata(id: 3, - name: "Directory 3", - type: .folder, - size: nil, - parentID: XCTUnwrap(folderMetadata.id), - lastModifiedDate: nil, - statusCode: .isUploaded, - cloudPath: CloudPath("/Directory 1/Directory 3"), - isPlaceholderItem: false) - let subFolderMetadataID = try XCTUnwrap(subFolderMetadata.id) - metadataManagerMock.cachedMetadata[subFolderMetadataID] = subFolderMetadata - - let getItemIdentifierPromise = adapter.getItemIdentifier(for: cloudPath) - XCTAssertRejects(getItemIdentifierPromise, with: NSFileProviderError(.noSuchItem)._nsError) - } - - func testGetItemIdentifierForItemNotYetCached() throws { + func testGetItemIdentifierFallsBackToEnumerationForColdPath() throws { + // Not cached, but exists in mock cloud; enumeration populates the cache, then lookup succeeds. let cloudPath = CloudPath("/Directory 1/Directory 2") let getItemIdentifierPromise = adapter.getItemIdentifier(for: cloudPath) wait(for: getItemIdentifierPromise, timeout: 5.0) let itemIdentifier = try XCTUnwrap(getItemIdentifierPromise.value) - let expectedItemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 7) - XCTAssertEqual(expectedItemIdentifier, itemIdentifier) + let resolvedMetadata = try XCTUnwrap(metadataManagerMock.getCachedMetadata(for: cloudPath)) + XCTAssertEqual(try NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: XCTUnwrap(resolvedMetadata.id)), itemIdentifier) + } + + func testGetItemIdentifierForNonexistentItem() { + // Neither cached nor in mock cloud; enumeration finds nothing, lookup rejects. + let cloudPath = CloudPath("/Directory 1/Does Not Exist") + let getItemIdentifierPromise = adapter.getItemIdentifier(for: cloudPath) + XCTAssertRejects(getItemIdentifierPromise, with: NSFileProviderError(.noSuchItem)._nsError) } } diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemTests.swift index 9d87bd029..4a8f91b4d 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterGetItemTests.swift @@ -26,7 +26,7 @@ class FileProviderAdapterGetItemTests: FileProviderAdapterTestCase { func testGetFileProviderItem() throws { let id: Int64 = 2 - let itemMetadata = ItemMetadata(id: id, name: "TestItem", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestItem"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: id, name: "TestItem", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) metadataManagerMock.cachedMetadata[id] = itemMetadata let item = try adapter.item(for: NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: id)) @@ -39,7 +39,7 @@ class FileProviderAdapterGetItemTests: FileProviderAdapterTestCase { func testGetFileProviderItemWithUploadError() throws { let id: Int64 = 2 - let itemMetadata = ItemMetadata(id: id, name: "TestItem", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: CloudPath("/TestItem"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(id: id, name: "TestItem", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: true) metadataManagerMock.cachedMetadata[id] = itemMetadata let uploadTask = UploadTaskRecord(correspondingItem: id, lastFailedUploadDate: Date(), uploadErrorCode: NSFileProviderError(.insufficientQuota).errorCode, uploadErrorDomain: NSFileProviderErrorDomain, uploadStartedAt: nil) diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift index 55019c1b2..22717894f 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterImportDocumentTests.swift @@ -35,7 +35,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { let fileURL = tmpDirectory.appendingPathComponent("ItemToBeImported.txt", isDirectory: false) let fileContent = "TestContent" try fileContent.write(to: fileURL, atomically: true, encoding: .utf8) - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let result = try adapter.localItemImport(fileURL: fileURL, parentIdentifier: .rootContainer) @@ -65,7 +65,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { let fileContent = "TestContent" try fileContent.write(to: fileURL, atomically: true, encoding: .utf8) - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) XCTAssertThrowsError(try adapter.localItemImport(fileURL: fileURL, parentIdentifier: .rootContainer)) { error in @@ -86,7 +86,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { try FileManager.default.createDirectory(at: expectedFileURL.deletingLastPathComponent(), withIntermediateDirectories: false) let existingFileContent = "ExistingFileContent" try existingFileContent.write(to: expectedFileURL, atomically: true, encoding: .utf8) - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) XCTAssertThrowsError(try adapter.localItemImport(fileURL: fileURL, parentIdentifier: .rootContainer)) { error in @@ -113,7 +113,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { func testImportDocument() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let fileURL = tmpDirectory.appendingPathComponent("ItemToBeImported.txt", isDirectory: false) @@ -158,7 +158,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { func testItemChanged() throws { let cloudPath = CloudPath("/Item.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "Item.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Item.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) metadataManagerMock.cachedMetadata[itemID] = itemMetadata let adapter = createFullyMockedAdapter() @@ -204,7 +204,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { cloudProviderMock.deleteFileAtReturnValue = deleteItemPromise cloudProviderMock.uploadFileFromToReplaceExistingReturnValue = uploadItemPromise - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let itemFolderURL = tmpDirectory.appendingPathComponent("\(itemID)", isDirectory: true) @@ -212,7 +212,7 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { let expectedFileURL = itemFolderURL.appendingPathComponent("File 1", isDirectory: false) let existingFileContent = "Existing Content" try existingFileContent.write(to: expectedFileURL, atomically: true, encoding: .utf8) - let existingItemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 1"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let existingItemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) try metadataManagerMock.cacheMetadata(existingItemMetadata) try cachedFileManagerMock.cacheLocalFileInfo(for: itemID, localURL: expectedFileURL, lastModifiedDate: Date(timeIntervalSince1970: 0)) @@ -282,16 +282,13 @@ class FileProviderAdapterImportDocumentTests: FileProviderAdapterTestCase { let fileURL = tmpDirectory.appendingPathComponent(nfdName, isDirectory: false) try "test".write(to: fileURL, atomically: true, encoding: .utf8) - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let result = try adapter.localItemImport(fileURL: fileURL, parentIdentifier: .rootContainer) let storedName = result.item.filename XCTAssertEqual(Array(storedName.utf8), Array(nfcName.utf8), "Filename should be stored in NFC form") - - let storedCloudPath = result.item.metadata.cloudPath.path - XCTAssertEqual(Array(storedCloudPath.utf8), Array("/\(nfcName)".utf8), "CloudPath should be stored in NFC form") } } diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterMoveItemTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterMoveItemTests.swift index d24d6bea0..c75953bd9 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterMoveItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterMoveItemTests.swift @@ -15,7 +15,7 @@ import XCTest class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { func testMoveItemLocally() throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let parentItemID: Int64 = 2 @@ -23,9 +23,9 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Folder/RenamedTest.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let targetParentCloudPath = CloudPath("/Folder/") - let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: targetParentCloudPath, isPlaceholderItem: false) + let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata([itemMetadata, newParentItemMetadata]) let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) @@ -37,7 +37,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(parentItemIdentifier, item.parentItemIdentifier) XCTAssertEqual(itemIdentifier, item.itemIdentifier) XCTAssertEqual(ItemStatus.isUploading, item.metadata.statusCode) - XCTAssertEqual(targetCloudPath, item.metadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, item.metadata.name) XCTAssertEqual(3, metadataManagerMock.cachedMetadata.count) XCTAssertEqual(itemMetadata, metadataManagerMock.cachedMetadata[itemID]) @@ -45,7 +45,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(newName, itemMetadata.name) XCTAssertEqual(newParentItemMetadata.id, itemMetadata.parentID) XCTAssertEqual(ItemStatus.isUploading, itemMetadata.statusCode) - XCTAssertEqual(targetCloudPath, itemMetadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, itemMetadata.name) let reparentTaskRecord = result.reparentTaskRecord XCTAssertEqual(itemID, reparentTaskRecord.correspondingItem) @@ -56,13 +56,13 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { } func testMoveItemLocallyOnlyNameChanged() throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/RenamedTest.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) metadataManagerMock.cachedMetadata[itemID] = itemMetadata let itemIdentifier = try NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: XCTUnwrap(itemMetadata.id)) let newName = "RenamedTest.txt" @@ -72,7 +72,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, item.parentItemIdentifier) XCTAssertEqual(itemIdentifier, item.itemIdentifier) XCTAssertEqual(ItemStatus.isUploading, item.metadata.statusCode) - XCTAssertEqual(targetCloudPath, item.metadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, item.metadata.name) XCTAssertEqual(2, metadataManagerMock.cachedMetadata.count) XCTAssertEqual(itemMetadata, metadataManagerMock.cachedMetadata[itemID]) @@ -80,7 +80,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(newName, itemMetadata.name) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainerDatabaseValue, itemMetadata.parentID) XCTAssertEqual(ItemStatus.isUploading, itemMetadata.statusCode) - XCTAssertEqual(targetCloudPath, itemMetadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, itemMetadata.name) let reparentTaskRecord = result.reparentTaskRecord XCTAssertEqual(itemID, reparentTaskRecord.correspondingItem) @@ -89,7 +89,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { } func testMoveItemLocallyOnlyParentChanged() throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let parentItemID: Int64 = 2 @@ -97,9 +97,9 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Folder/Test.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let targetParentCloudPath = CloudPath("/Folder/") - let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: targetParentCloudPath, isPlaceholderItem: false) + let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata([itemMetadata, newParentItemMetadata]) let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) @@ -110,7 +110,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(parentItemIdentifier, item.parentItemIdentifier) XCTAssertEqual(itemIdentifier, item.itemIdentifier) XCTAssertEqual(ItemStatus.isUploading, item.metadata.statusCode) - XCTAssertEqual(targetCloudPath, item.metadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, item.metadata.name) XCTAssertEqual(3, metadataManagerMock.cachedMetadata.count) XCTAssertEqual(itemMetadata, metadataManagerMock.cachedMetadata[itemID]) @@ -118,7 +118,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual("Test.txt", itemMetadata.name) XCTAssertEqual(parentItemID, itemMetadata.parentID) XCTAssertEqual(ItemStatus.isUploading, itemMetadata.statusCode) - XCTAssertEqual(targetCloudPath, itemMetadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, itemMetadata.name) let reparentTaskRecord = result.reparentTaskRecord XCTAssertEqual(itemID, reparentTaskRecord.correspondingItem) @@ -128,16 +128,94 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(targetCloudPath, reparentTaskRecord.targetCloudPath) } + func testMoveFolderLocallyUpdatesDescendantCloudPaths() throws { + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + try metadataManagerMock.cacheMetadata(rootItemMetadata) + + let sourceParentID: Int64 = 2 + let movedFolderID: Int64 = 3 + let childFileID: Int64 = 4 + let targetParentID: Int64 = 5 + + // Initial tree: + // / + // |- A/ + // | |- B/ + // | |- C.txt + // |- Target/ + let sourceParent = ItemMetadata(id: sourceParentID, name: "A", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let movedFolder = ItemMetadata(id: movedFolderID, name: "B", type: .folder, size: nil, parentID: sourceParentID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let childFile = ItemMetadata(id: childFileID, name: "C.txt", type: .file, size: 100, parentID: movedFolderID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let targetParent = ItemMetadata(id: targetParentID, name: "Target", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + try metadataManagerMock.cacheMetadata([sourceParent, movedFolder, childFile, targetParent]) + + let movedFolderIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: movedFolderID) + let targetParentIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: targetParentID) + + // Move B from /A/B/ to /Target/B/. + // Because cloudPath is derived from the parentID chain, the descendant must follow the move automatically. + _ = try adapter.moveItemLocally(withIdentifier: movedFolderIdentifier, toParentItemWithIdentifier: targetParentIdentifier, newName: nil) + + // Sanity check for the folder row itself. + XCTAssertEqual(targetParentID, movedFolder.parentID) + XCTAssertEqual(CloudPath("/Target/B"), try metadataManagerMock.getCloudPath(for: movedFolderID)) + + // Regression check for #450: + // The child's parentID is unchanged, but its derived cloudPath must reflect the new chain. + // Otherwise path-based metadata lookups, subtree queries, enumeration, deletion bookkeeping, + // and follow-up remote operations can use the stale location. + XCTAssertEqual(movedFolderID, childFile.parentID) + XCTAssertEqual(CloudPath("/Target/B/C.txt"), try metadataManagerMock.getCloudPath(for: childFileID)) + XCTAssertEqual(childFileID, try metadataManagerMock.getCachedMetadata(for: CloudPath("/Target/B/C.txt"))?.id) + XCTAssertNil(try metadataManagerMock.getCachedMetadata(for: CloudPath("/A/B/C.txt"))) + } + + func testMoveFolderLocallyUpdatesDeepDescendantCloudPaths() throws { + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + try metadataManagerMock.cacheMetadata(rootItemMetadata) + + let sourceParentID: Int64 = 2 + let movedFolderID: Int64 = 3 + let middleFolderID: Int64 = 4 + let deepFileID: Int64 = 5 + let targetParentID: Int64 = 6 + + // Initial tree: + // / + // |- A/ + // | |- B/ + // | |- C/ + // | |- D.txt + // |- Target/ + let sourceParent = ItemMetadata(id: sourceParentID, name: "A", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let movedFolder = ItemMetadata(id: movedFolderID, name: "B", type: .folder, size: nil, parentID: sourceParentID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let middleFolder = ItemMetadata(id: middleFolderID, name: "C", type: .folder, size: nil, parentID: movedFolderID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let deepFile = ItemMetadata(id: deepFileID, name: "D.txt", type: .file, size: 100, parentID: middleFolderID, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let targetParent = ItemMetadata(id: targetParentID, name: "Target", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + try metadataManagerMock.cacheMetadata([sourceParent, movedFolder, middleFolder, deepFile, targetParent]) + + let movedFolderIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: movedFolderID) + let targetParentIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: targetParentID) + _ = try adapter.moveItemLocally(withIdentifier: movedFolderIdentifier, toParentItemWithIdentifier: targetParentIdentifier, newName: nil) + + XCTAssertEqual(targetParentID, movedFolder.parentID) + XCTAssertEqual(CloudPath("/Target/B"), try metadataManagerMock.getCloudPath(for: movedFolderID)) + XCTAssertEqual(movedFolderID, middleFolder.parentID) + XCTAssertEqual(CloudPath("/Target/B/C"), try metadataManagerMock.getCloudPath(for: middleFolderID)) + XCTAssertEqual(middleFolderID, deepFile.parentID) + XCTAssertEqual(CloudPath("/Target/B/C/D.txt"), try metadataManagerMock.getCloudPath(for: deepFileID)) + } + func testRenameItem() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/RenamedTest.txt") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) metadataManagerMock.cachedMetadata[itemID] = itemMetadata let newName = "RenamedTest.txt" let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) @@ -152,7 +230,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, fileProviderItem.parentItemIdentifier) XCTAssertEqual(itemIdentifier, fileProviderItem.itemIdentifier) XCTAssertEqual(ItemStatus.isUploading, fileProviderItem.metadata.statusCode) - XCTAssertEqual(targetCloudPath, fileProviderItem.metadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, fileProviderItem.metadata.name) XCTAssertEqual(2, self.metadataManagerMock.cachedMetadata.count) XCTAssertEqual(itemMetadata, self.metadataManagerMock.cachedMetadata[itemID]) @@ -160,7 +238,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(newName, itemMetadata.name) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainerDatabaseValue, itemMetadata.parentID) XCTAssertEqual(ItemStatus.isUploading, itemMetadata.statusCode) - XCTAssertEqual(targetCloudPath, itemMetadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, itemMetadata.name) guard let reparentTaskRecord = self.reparentTaskManagerMock.reparentTasks[itemID] else { XCTFail("reparentTaskRecord is nil") @@ -177,7 +255,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { func testReparentItem() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let parentItemID: Int64 = 2 @@ -185,9 +263,9 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Folder/Test.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let targetParentCloudPath = CloudPath("/Folder/") - let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: targetParentCloudPath, isPlaceholderItem: false) + let newParentItemMetadata = ItemMetadata(id: parentItemID, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata([itemMetadata, newParentItemMetadata]) let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: itemID) @@ -202,7 +280,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual(parentItemIdentifier, fileProviderItem.parentItemIdentifier) XCTAssertEqual(itemIdentifier, fileProviderItem.itemIdentifier) XCTAssertEqual(ItemStatus.isUploading, fileProviderItem.metadata.statusCode) - XCTAssertEqual(targetCloudPath, fileProviderItem.metadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, fileProviderItem.metadata.name) XCTAssertEqual(3, self.metadataManagerMock.cachedMetadata.count) XCTAssertEqual(itemMetadata, self.metadataManagerMock.cachedMetadata[itemID]) @@ -210,7 +288,7 @@ class FileProviderAdapterMoveItemTests: FileProviderAdapterTestCase { XCTAssertEqual("Test.txt", itemMetadata.name) XCTAssertEqual(parentItemID, itemMetadata.parentID) XCTAssertEqual(ItemStatus.isUploading, itemMetadata.statusCode) - XCTAssertEqual(targetCloudPath, itemMetadata.cloudPath) + XCTAssertEqual(targetCloudPath.lastPathComponent, itemMetadata.name) guard let reparentTaskRecord = self.reparentTaskManagerMock.reparentTasks[itemID] else { XCTFail("reparentTaskRecord is nil") diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift index 8e2547cff..725f63ef6 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterRecoverUploadsTests.swift @@ -42,7 +42,7 @@ class FileProviderAdapterRecoverUploadsTests: FileProviderAdapterTestCase { func testRecoverStuckUploads_marksErrorWhenLocalFileMissing() throws { let cloudPath = CloudPath("/File.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "File.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let taskRecord = UploadTaskRecord(correspondingItem: itemID, lastFailedUploadDate: nil, uploadErrorCode: nil, uploadErrorDomain: nil, uploadStartedAt: Date()) @@ -60,7 +60,7 @@ class FileProviderAdapterRecoverUploadsTests: FileProviderAdapterTestCase { func testRecoverStuckUploads_reschedulesWhenLocalFileExists() throws { let cloudPath = CloudPath("/File.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "File.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) // Create a local file @@ -83,7 +83,7 @@ class FileProviderAdapterRecoverUploadsTests: FileProviderAdapterTestCase { func testRecoverStuckUploads_retriesConnectivityFailedUploads() throws { let cloudPath = CloudPath("/OfflineFile.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "OfflineFile.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(id: itemID, name: "OfflineFile.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: true) try metadataManagerMock.cacheMetadata(itemMetadata) // Create a local file @@ -131,7 +131,7 @@ class FileProviderAdapterRecoverUploadsTests: FileProviderAdapterTestCase { func testRecoverStuckUploads_marksErrorForRetryableUploadWithMissingLocalFile() throws { let cloudPath = CloudPath("/OfflineFile.txt") - let itemMetadata = ItemMetadata(id: itemID, name: "OfflineFile.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(id: itemID, name: "OfflineFile.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: true) try metadataManagerMock.cacheMetadata(itemMetadata) // No cached file info exists diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetFavoriteRankTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetFavoriteRankTests.swift index 935951271..b583bca0d 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetFavoriteRankTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetFavoriteRankTests.swift @@ -13,7 +13,7 @@ import XCTest class FileProviderAdapterSetFavoriteRankTests: FileProviderAdapterTestCase { func testSetFavoriteRank() { let expectation = XCTestExpectation() - metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) + metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) let favoriteRank: NSNumber = 100 let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 2) adapter.setFavoriteRank(favoriteRank, forItemIdentifier: itemIdentifier) { item, error in diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetTagDataTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetTagDataTests.swift index 0e5dba773..a0a52d91c 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetTagDataTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterSetTagDataTests.swift @@ -15,7 +15,7 @@ class FileProviderAdapterSetTagDataTests: FileProviderAdapterTestCase { let itemIdentifier = NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 2) func testSetTagData() { let expectation = XCTestExpectation() - metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) + metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) let tagData = Data("Foo".utf8) adapter.setTagData(tagData, forItemIdentifier: itemIdentifier) { item, error in @@ -29,7 +29,7 @@ class FileProviderAdapterSetTagDataTests: FileProviderAdapterTestCase { func testSetEmptyTagData() { let expectation = XCTestExpectation() - metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) + metadataManagerMock.cachedMetadata[2] = ItemMetadata(id: 2, name: "Test", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false, favoriteRank: nil, tagData: nil) let emptyTagData = Data() adapter.setTagData(emptyTagData, forItemIdentifier: itemIdentifier) { item, error in XCTAssertNil(error) diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterStartProvidingItemTests.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterStartProvidingItemTests.swift index ccdacbbdf..b1e4b7f5a 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterStartProvidingItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterStartProvidingItemTests.swift @@ -14,7 +14,7 @@ import XCTest class FileProviderAdapterStartProvidingItemTests: FileProviderAdapterTestCase { private let itemID: Int64 = 2 private let cloudPath = CloudPath("/File 1") - private lazy var itemMetadata = ItemMetadata(id: 2, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + private lazy var itemMetadata = ItemMetadata(id: 2, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) private lazy var itemDirectory = tmpDirectory.appendingPathComponent("/\(itemID)") private lazy var url = itemDirectory.appendingPathComponent("File 1") @@ -70,7 +70,7 @@ class FileProviderAdapterStartProvidingItemTests: FileProviderAdapterTestCase { func testStartProvidingItemWithConflictingLocalVersion() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) // Create local folder for conflicting item diff --git a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift index c15555f00..a58f7a953 100644 --- a/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift +++ b/CryptomatorFileProviderTests/FileProviderAdapter/FileProviderAdapterTestCase.swift @@ -6,6 +6,7 @@ // Copyright © 2021 Skymatic GmbH. All rights reserved. // +import CryptomatorCloudAccessCore import Dependencies import Foundation import Promises @@ -53,7 +54,8 @@ class FileProviderAdapterTestCase: CloudTaskExecutorTestCase { uploadTaskManagerMock.getTaskForOnURLSessionTaskCreationClosure = { let id = $0.correspondingItem let metadata = try XCTUnwrap(self.metadataManagerMock.cachedMetadata[id]) - return UploadTask(taskRecord: $0, itemMetadata: metadata, onURLSessionTaskCreation: $1) + let cloudPath = (try? self.metadataManagerMock.getCloudPath(for: id)) ?? CloudPath("/") + return UploadTask(taskRecord: $0, itemMetadata: metadata, cloudPath: cloudPath, onURLSessionTaskCreation: $1) } } diff --git a/CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift b/CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift index 22a6cfadc..0d696eac7 100644 --- a/CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift +++ b/CryptomatorFileProviderTests/FileProviderEnumeratorTests.swift @@ -26,8 +26,8 @@ class FileProviderEnumeratorTestCase: XCTestCase { let dbPath = FileManager.default.temporaryDirectory let domain = NSFileProviderDomain(vaultUID: "VaultUID-12345", displayName: "Test Vault") let items: [FileProviderItem] = [ - .init(metadata: ItemMetadata(id: 2, name: "Test.txt", type: .file, size: 100, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test.txt"), isPlaceholderItem: false), domainIdentifier: .test), - .init(metadata: ItemMetadata(id: 3, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestFolder"), isPlaceholderItem: false), domainIdentifier: .test) + .init(metadata: ItemMetadata(id: 2, name: "Test.txt", type: .file, size: 100, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + .init(metadata: ItemMetadata(id: 3, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test) ] let deleteItemIdentifiers = [1, 2, 3].map { NSFileProviderItemIdentifier("\($0)") } diff --git a/CryptomatorFileProviderTests/FileProviderItemTests.swift b/CryptomatorFileProviderTests/FileProviderItemTests.swift index 17cc4a4a6..de2b5e51c 100644 --- a/CryptomatorFileProviderTests/FileProviderItemTests.swift +++ b/CryptomatorFileProviderTests/FileProviderItemTests.swift @@ -16,7 +16,7 @@ import XCTest class FileProviderItemTests: XCTestCase { func testRootItem() { let cloudPath = CloudPath("/") - let metadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "root", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "root", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, item.itemIdentifier) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, item.parentItemIdentifier) @@ -25,7 +25,7 @@ class FileProviderItemTests: XCTestCase { func testFileItem() { let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) XCTAssertEqual(NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 2), item.itemIdentifier) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, item.parentItemIdentifier) @@ -40,7 +40,7 @@ class FileProviderItemTests: XCTestCase { func testFolderItem() { let cloudPath = CloudPath("/test Folder/") - let metadata = ItemMetadata(id: 2, name: "test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) XCTAssertEqual(NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: 2), item.itemIdentifier) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainer, item.parentItemIdentifier) @@ -55,7 +55,7 @@ class FileProviderItemTests: XCTestCase { func testUploadError() { let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) let lastFailedUploadDate = Date(timeIntervalSinceReferenceDate: 0) let failedUploadTask = UploadTaskRecord(correspondingItem: 2, lastFailedUploadDate: lastFailedUploadDate, uploadErrorCode: NSFileProviderError.insufficientQuota.rawValue, uploadErrorDomain: NSFileProviderErrorDomain, uploadStartedAt: nil) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test, error: failedUploadTask.failedWithError) @@ -75,7 +75,7 @@ class FileProviderItemTests: XCTestCase { func testIsDownloadedOnlyForLocallyExistingFile() throws { let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let tmpDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) try FileManager.default.createDirectory(at: tmpDir, withIntermediateDirectories: false) @@ -114,7 +114,7 @@ class FileProviderItemTests: XCTestCase { $0.permissionProvider = permissionProviderMock } operation: { let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let capabilities: [NSFileProviderItemCapabilities] = [.allowsAddingSubItems, .allowsContentEnumerating, .allowsDeleting, .allowsReading, .allowsReparenting, .allowsWriting] @@ -147,7 +147,7 @@ class FileProviderItemTests: XCTestCase { func testEvictFileFromCacheActionDisabledForNotCachedFile() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isDownloading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isDownloading, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertFalse(userInfo["enableEvictFileFromCacheAction"] as? Bool ?? true) @@ -155,7 +155,7 @@ class FileProviderItemTests: XCTestCase { func testEvictFileFromCacheActionDisabledForFolder() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isDownloading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isDownloading, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertFalse(userInfo["enableEvictFileFromCacheAction"] as? Bool ?? true) @@ -165,7 +165,7 @@ class FileProviderItemTests: XCTestCase { func testRetryFailedUploadActionEnabled() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test, error: NSFileProviderError(.insufficientQuota)._nsError) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertTrue(userInfo["enableRetryFailedUploadAction"] as? Bool ?? false) @@ -173,7 +173,7 @@ class FileProviderItemTests: XCTestCase { func testRetryFailedUploadActionDisabled() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertFalse(userInfo["enableRetryFailedUploadAction"] as? Bool ?? true) @@ -183,7 +183,7 @@ class FileProviderItemTests: XCTestCase { func testRetryWaitingUploadActionEnabled() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let userInfo = try XCTUnwrap(item.userInfo) XCTAssert(userInfo["enableRetryWaitingUploadAction"] as? Bool ?? false) @@ -191,7 +191,7 @@ class FileProviderItemTests: XCTestCase { func testRetryWaitingUploadActionDisabledForFolder() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertFalse(userInfo["enableRetryWaitingUploadAction"] as? Bool ?? true) @@ -199,7 +199,7 @@ class FileProviderItemTests: XCTestCase { func testRetryWaitingUploadActionDisabledForUploadError() throws { let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) let item = FileProviderItem(metadata: metadata, domainIdentifier: .test, error: NSFileProviderError(.insufficientQuota)._nsError) let userInfo = try XCTUnwrap(item.userInfo) XCTAssertFalse(userInfo["enableRetryWaitingUploadAction"] as? Bool ?? true) @@ -212,7 +212,7 @@ class FileProviderItemTests: XCTestCase { try "Foo".write(to: localURL, atomically: true, encoding: .utf8) let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: statusCode, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: statusCode, isPlaceholderItem: false) return FileProviderItem(metadata: metadata, domainIdentifier: .test, localURL: localURL) } } diff --git a/CryptomatorFileProviderTests/FileProviderNotificatorTests.swift b/CryptomatorFileProviderTests/FileProviderNotificatorTests.swift index e72a9e982..dfbee9a51 100644 --- a/CryptomatorFileProviderTests/FileProviderNotificatorTests.swift +++ b/CryptomatorFileProviderTests/FileProviderNotificatorTests.swift @@ -18,7 +18,7 @@ class FileProviderNotificatorTests: XCTestCase { let updatedMetadataIDs: [Int64] = [2, 3, 4] lazy var updatedItemIdentifiers = updatedMetadataIDs.map { NSFileProviderItemIdentifier(domainIdentifier: .test, itemID: $0) } lazy var updatedItems: [FileProviderItem] = updatedMetadataIDs.map { - FileProviderItem(metadata: ItemMetadata(id: $0, name: "\($0)", type: .file, size: nil, parentID: 0, lastModifiedDate: nil, statusCode: .isDownloading, cloudPath: CloudPath("/\($0)"), isPlaceholderItem: false), domainIdentifier: .test) + FileProviderItem(metadata: ItemMetadata(id: $0, name: "\($0)", type: .file, size: nil, parentID: 0, lastModifiedDate: nil, statusCode: .isDownloading, isPlaceholderItem: false), domainIdentifier: .test) } override func setUpWithError() throws { diff --git a/CryptomatorFileProviderTests/Middleware/ErrorMapperTests.swift b/CryptomatorFileProviderTests/Middleware/ErrorMapperTests.swift index 4fc3174ea..d1f7a2c2c 100644 --- a/CryptomatorFileProviderTests/Middleware/ErrorMapperTests.swift +++ b/CryptomatorFileProviderTests/Middleware/ErrorMapperTests.swift @@ -54,5 +54,9 @@ class ErrorMapperTests: XCTestCase { var itemMetadata: ItemMetadata { fatalError("not implemented") } + + var cloudPath: CloudPath { + fatalError("not implemented") + } } } diff --git a/CryptomatorFileProviderTests/Middleware/OnlineItemNameCollisionHandlerTests.swift b/CryptomatorFileProviderTests/Middleware/OnlineItemNameCollisionHandlerTests.swift index d3517b90e..0a189d88c 100644 --- a/CryptomatorFileProviderTests/Middleware/OnlineItemNameCollisionHandlerTests.swift +++ b/CryptomatorFileProviderTests/Middleware/OnlineItemNameCollisionHandlerTests.swift @@ -39,15 +39,15 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { let expectation = XCTestExpectation() let originalCloudPath = CloudPath("/foo.txt") let workflowMock = WorkflowMiddlewareMock { task in - if task.itemMetadata.cloudPath == originalCloudPath { + if task.cloudPath == originalCloudPath { return Promise(CloudProviderError.itemAlreadyExists) } return Promise(()) } - let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: originalCloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) middleware.setNext(AnyWorkflowMiddleware(workflowMock)) - middleware.execute(task: SampleCloudTask(itemMetadata: itemMetadata)).then { + middleware.execute(task: FolderCreationTask(itemMetadata: itemMetadata, cloudPath: originalCloudPath)).then { guard let itemID = itemMetadata.id, let cachedItemMetadata = try self.itemMetadataManager.getCachedMetadata(for: itemID) else { XCTFail("ItemMetadata not found in DB") return @@ -59,9 +59,10 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { XCTAssert(cachedItemMetadata.name.hasSuffix(").txt")) XCTAssertEqual(15, cachedItemMetadata.name.count) - XCTAssert(cachedItemMetadata.cloudPath.path.hasPrefix("/foo (")) - XCTAssert(cachedItemMetadata.cloudPath.path.hasSuffix(").txt")) - XCTAssertEqual(16, cachedItemMetadata.cloudPath.path.count) + let cachedCloudPath = (try? self.itemMetadataManager.getCloudPath(for: itemID)) ?? CloudPath("/") + XCTAssert(cachedCloudPath.path.hasPrefix("/foo (")) + XCTAssert(cachedCloudPath.path.hasSuffix(").txt")) + XCTAssertEqual(16, cachedCloudPath.path.count) // Check that the remaining item metadata properties have not changed. @@ -85,10 +86,11 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { let workflowMock = WorkflowMiddlewareMock { _ in return Promise(CloudProviderError.itemAlreadyExists) } - let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: CloudPath("/foo.txt"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) middleware.setNext(AnyWorkflowMiddleware(workflowMock)) - middleware.execute(task: SampleCloudTask(itemMetadata: itemMetadata)).then { + let sampleCloudPath = CloudPath("/foo.txt") + middleware.execute(task: FolderCreationTask(itemMetadata: itemMetadata, cloudPath: sampleCloudPath)).then { XCTFail("Promise fulfilled") }.catch { error in guard case CloudProviderError.itemAlreadyExists = error else { @@ -105,9 +107,10 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { XCTAssert(cachedItemMetadata.name.hasSuffix(").txt")) XCTAssertEqual(15, cachedItemMetadata.name.count) - XCTAssert(cachedItemMetadata.cloudPath.path.hasPrefix("/foo (")) - XCTAssert(cachedItemMetadata.cloudPath.path.hasSuffix(").txt")) - XCTAssertEqual(16, cachedItemMetadata.cloudPath.path.count) + let cachedCloudPath = (try? self.itemMetadataManager.getCloudPath(for: itemID)) ?? CloudPath("/") + XCTAssert(cachedCloudPath.path.hasPrefix("/foo (")) + XCTAssert(cachedCloudPath.path.hasSuffix(").txt")) + XCTAssertEqual(16, cachedCloudPath.path.count) // Check that the remaining item metadata properties have not changed. XCTAssertEqual(CloudItemType.file, cachedItemMetadata.type) @@ -126,10 +129,11 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { let workflowMock = WorkflowMiddlewareMock { _ in return Promise(CloudProviderError.itemNotFound) } - let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: CloudPath("/foo.txt"), isPlaceholderItem: true) + let itemMetadata = ItemMetadata(name: "foo.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try itemMetadataManager.cacheMetadata(itemMetadata) middleware.setNext(AnyWorkflowMiddleware(workflowMock)) - middleware.execute(task: SampleCloudTask(itemMetadata: itemMetadata)).then { + let sampleCloudPath = CloudPath("/foo.txt") + middleware.execute(task: FolderCreationTask(itemMetadata: itemMetadata, cloudPath: sampleCloudPath)).then { XCTFail("Promise fulfilled") }.catch { error in guard case CloudProviderError.itemNotFound = error else { @@ -143,7 +147,6 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { // Check that the item metadata properties have not changed. XCTAssertEqual("foo.txt", cachedItemMetadata.name) - XCTAssertEqual(CloudPath("/foo.txt"), cachedItemMetadata.cloudPath) XCTAssertEqual(CloudItemType.file, cachedItemMetadata.type) XCTAssertNil(cachedItemMetadata.size) XCTAssertEqual(NSFileProviderItemIdentifier.rootContainerDatabaseValue, cachedItemMetadata.parentID) @@ -154,8 +157,4 @@ class OnlineItemNameCollisionHandlerTests: XCTestCase { } wait(for: [expectation], timeout: 5.0) } - - private struct SampleCloudTask: CloudTask { - let itemMetadata: ItemMetadata - } } diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/CloudTaskExecutorTestCase.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/CloudTaskExecutorTestCase.swift index 7626bf4d2..8a67e346a 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/CloudTaskExecutorTestCase.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/CloudTaskExecutorTestCase.swift @@ -32,7 +32,9 @@ class CloudTaskExecutorTestCase: XCTestCase { reparentTaskManagerMock = ReparentTaskManagerMock() deletionTaskManagerMock = DeletionTaskManagerMock() itemEnumerationTaskManagerMock = ItemEnumerationTaskManagerMock() + itemEnumerationTaskManagerMock.itemMetadataManager = metadataManagerMock downloadTaskManagerMock = DownloadTaskManagerMock() + downloadTaskManagerMock.itemMetadataManager = metadataManagerMock deleteItemHelper = DeleteItemHelper(itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock) tmpDirectory = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true).appendingPathComponent(UUID().uuidString, isDirectory: true) try FileManager.default.createDirectory(at: tmpDirectory, withIntermediateDirectories: false, attributes: nil) @@ -51,7 +53,7 @@ class CloudTaskExecutorTestCase: XCTestCase { var setFavoriteRank = [Int64: Int64?]() func cacheMetadata(_ metadata: ItemMetadata) throws { - if let cachedItemMetadata = try getCachedMetadata(for: metadata.cloudPath) { + if let cachedItemMetadata = try childOfFolder(parentID: metadata.parentID, name: metadata.name), cachedItemMetadata.id != NSFileProviderItemIdentifier.rootContainerDatabaseValue { metadata.id = cachedItemMetadata.id metadata.statusCode = cachedItemMetadata.statusCode metadata.tagData = cachedItemMetadata.tagData @@ -75,6 +77,9 @@ class CloudTaskExecutorTestCase: XCTestCase { func updateMetadata(_ metadata: ItemMetadata) throws { updatedMetadata.append(metadata) + if let id = metadata.id { + cachedMetadata[id] = metadata + } } func cacheMetadata(_ itemMetadataList: [ItemMetadata]) throws { @@ -83,8 +88,49 @@ class CloudTaskExecutorTestCase: XCTestCase { } } + func getCloudPath(for id: Int64) throws -> CloudPath { + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if id == rootID { + return CloudPath("/") + } + var components = [String]() + var currentID = id + var depth = 0 + while currentID != rootID { + guard let metadata = cachedMetadata[currentID] else { + throw FileProviderAdapterError.itemNotFound + } + components.insert(metadata.name, at: 0) + currentID = metadata.parentID + depth += 1 + if depth > 1024 { + throw FileProviderAdapterError.unresolvableParentChain + } + } + return components.reduce(CloudPath("/")) { $0.appendingPathComponent($1) } + } + func getCachedMetadata(for cloudPath: CloudPath) throws -> ItemMetadata? { - cachedMetadata.first(where: { $1.cloudPath == cloudPath })?.value + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if cloudPath == CloudPath("/") { + return cachedMetadata[rootID] + } + var currentID = rootID + var current: ItemMetadata? + let components = cloudPath.pathComponents.dropFirst() + for component in components { + guard let child = try childOfFolder(parentID: currentID, name: String(component)) else { + return nil + } + currentID = child.id! + current = child + } + return current + } + + private func childOfFolder(parentID: Int64, name: String) throws -> ItemMetadata? { + let lowercasedName = name.lowercased() + return cachedMetadata.values.first(where: { $0.parentID == parentID && $0.name.lowercased() == lowercasedName && $0.id != NSFileProviderItemIdentifier.rootContainerDatabaseValue }) } func getPlaceholderMetadata(withParentID parentID: Int64) throws -> [ItemMetadata] { @@ -133,15 +179,17 @@ class CloudTaskExecutorTestCase: XCTestCase { } func getAllCachedMetadata(inside parent: ItemMetadata) throws -> [ItemMetadata] { + let rootID = NSFileProviderItemIdentifier.rootContainerDatabaseValue + if parent.id == rootID { + return cachedMetadata.values.filter { $0.id != rootID } + } var result = [ItemMetadata]() - for metadata in cachedMetadata.values { - if metadata.id == parent.id { - continue - } - if metadata.parentID == parent.id { - result.append(metadata) - } else if metadata.cloudPath.path.hasPrefix(parent.cloudPath.path) { + var queue: [Int64] = [parent.id!] + while let current = queue.first { + queue.removeFirst() + for metadata in cachedMetadata.values where metadata.parentID == current && metadata.id != current { result.append(metadata) + queue.append(metadata.id!) } } return result @@ -260,8 +308,8 @@ class CloudTaskExecutorTestCase: XCTestCase { var deletionTasks = [Int64: DeletionTaskRecord]() var associatedItemMetadata = [Int64: ItemMetadata]() - func createTaskRecord(for item: ItemMetadata) throws -> DeletionTaskRecord { - let deletionTask = DeletionTaskRecord(correspondingItem: item.id!, cloudPath: item.cloudPath, parentID: item.parentID, itemType: item.type) + func createTaskRecord(for item: ItemMetadata, cloudPath: CloudPath) throws -> DeletionTaskRecord { + let deletionTask = DeletionTaskRecord(correspondingItem: item.id!, cloudPath: cloudPath, parentID: item.parentID, itemType: item.type) deletionTasks[item.id!] = deletionTask associatedItemMetadata[item.id!] = item return deletionTask @@ -290,7 +338,7 @@ class CloudTaskExecutorTestCase: XCTestCase { guard let itemMetadata = associatedItemMetadata[deletionTask.correspondingItem] else { throw DBManagerError.missingItemMetadata } - return DeletionTask(taskRecord: deletionTask, itemMetadata: itemMetadata) + return DeletionTask(taskRecord: deletionTask, itemMetadata: itemMetadata, cloudPath: deletionTask.cloudPath) } } @@ -299,13 +347,20 @@ class CloudTaskExecutorTestCase: XCTestCase { var reparentTasks = [Int64: ReparentTaskRecord]() var removedReparentTasks = [ReparentTaskRecord]() - func createTaskRecord(for itemMetadata: ItemMetadata, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord { - let taskRecord = ReparentTaskRecord(correspondingItem: itemMetadata.id!, sourceCloudPath: itemMetadata.cloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: newParentID) + func createTaskRecord(for itemMetadata: ItemMetadata, sourceCloudPath: CloudPath, targetCloudPath: CloudPath, newParentID: Int64) throws -> ReparentTaskRecord { + let taskRecord = ReparentTaskRecord(correspondingItem: itemMetadata.id!, sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: newParentID) reparentTasks[itemMetadata.id!] = taskRecord associatedItemMetadata[itemMetadata.id!] = itemMetadata return taskRecord } + func renameItemAndUpdateReparentTask(itemMetadata: ItemMetadata, newName: String, targetCloudPath: CloudPath, reparentRecord: ReparentTaskRecord) throws -> ReparentTaskRecord { + itemMetadata.name = newName + let refreshed = ReparentTaskRecord(correspondingItem: reparentRecord.correspondingItem, sourceCloudPath: reparentRecord.sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: reparentRecord.oldParentID, newParentID: reparentRecord.newParentID) + reparentTasks[reparentRecord.correspondingItem] = refreshed + return refreshed + } + func removeTaskRecord(_ task: ReparentTaskRecord) throws { removedReparentTasks.append(task) } @@ -330,7 +385,7 @@ class CloudTaskExecutorTestCase: XCTestCase { guard let itemMetadata = associatedItemMetadata[reparentTask.correspondingItem] else { throw DBManagerError.missingItemMetadata } - return ReparentTask(taskRecord: reparentTask, itemMetadata: itemMetadata) + return ReparentTask(taskRecord: reparentTask, itemMetadata: itemMetadata, cloudPath: reparentTask.sourceCloudPath) } } @@ -338,9 +393,13 @@ class CloudTaskExecutorTestCase: XCTestCase { var removedTaskRecords = [ItemEnumerationTaskRecord]() var createdTasks = [ItemEnumerationTask]() + var itemMetadataManager: ItemMetadataManager? + var stubCloudPath: CloudPath? + func createTask(for item: ItemMetadata, pageToken: String?) throws -> ItemEnumerationTask { let taskRecord = ItemEnumerationTaskRecord(correspondingItem: item.id!, pageToken: pageToken) - let task = ItemEnumerationTask(taskRecord: taskRecord, itemMetadata: item) + let cloudPath = try stubCloudPath ?? itemMetadataManager?.getCloudPath(for: item.id!) ?? CloudPath("/") + let task = ItemEnumerationTask(taskRecord: taskRecord, itemMetadata: item, cloudPath: cloudPath) createdTasks.append(task) return task } @@ -353,9 +412,13 @@ class CloudTaskExecutorTestCase: XCTestCase { class DownloadTaskManagerMock: DownloadTaskManager { var removedTasks = [DownloadTaskRecord]() + var itemMetadataManager: ItemMetadataManager? + var stubCloudPath: CloudPath? + func createTask(for item: ItemMetadata, replaceExisting: Bool, localURL: URL, onURLSessionTaskCreation: CryptomatorFileProvider.URLSessionTaskCreationClosure?) throws -> DownloadTask { let taskRecord = DownloadTaskRecord(correspondingItem: item.id!, replaceExisting: replaceExisting, localURL: localURL) - return DownloadTask(taskRecord: taskRecord, itemMetadata: item, onURLSessionTaskCreation: onURLSessionTaskCreation) + let cloudPath = try stubCloudPath ?? itemMetadataManager?.getCloudPath(for: item.id!) ?? CloudPath("/") + return DownloadTask(taskRecord: taskRecord, itemMetadata: item, cloudPath: cloudPath, onURLSessionTaskCreation: onURLSessionTaskCreation) } func removeTaskRecord(_ task: DownloadTaskRecord) throws { diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/DeletionTaskExecutorTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/DeletionTaskExecutorTests.swift index e741fc2e8..07763fe83 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/DeletionTaskExecutorTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/DeletionTaskExecutorTests.swift @@ -20,8 +20,8 @@ class DeletionTaskExecutorTests: CloudTaskExecutorTestCase { func testDeleteFile() throws { let expectation = XCTestExpectation() - let itemMetadata = ItemMetadata(id: 2, name: "TestFile", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestFile"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) - let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata) + let itemMetadata = ItemMetadata(id: 2, name: "TestFile", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let deletionTask = try deletionTaskManagerMock.getTask(for: taskRecord) taskExecutor.execute(task: deletionTask).then { XCTAssertEqual(1, self.cloudProviderMock.deletedFiles.count) @@ -39,8 +39,8 @@ class DeletionTaskExecutorTests: CloudTaskExecutorTestCase { func testDeleteFolder() throws { let expectation = XCTestExpectation() - let itemMetadata = ItemMetadata(id: 2, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestFolder"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) - let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata) + let itemMetadata = ItemMetadata(id: 2, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let deletionTask = try deletionTaskManagerMock.getTask(for: taskRecord) taskExecutor.execute(task: deletionTask).then { XCTAssertEqual(1, self.cloudProviderMock.deletedFolders.count) @@ -63,8 +63,8 @@ class DeletionTaskExecutorTests: CloudTaskExecutorTestCase { } let expectation = XCTestExpectation() - let itemMetadata = ItemMetadata(id: 2, name: "TestFile", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestFile"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) - let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata) + let itemMetadata = ItemMetadata(id: 2, name: "TestFile", type: .file, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let deletionTask = try deletionTaskManagerMock.getTask(for: taskRecord) let taskExecutor = DeletionTaskExecutor(provider: errorCloudProviderMock, itemMetadataManager: metadataManagerMock) taskExecutor.execute(task: deletionTask).then { @@ -88,8 +88,8 @@ class DeletionTaskExecutorTests: CloudTaskExecutorTestCase { } let expectation = XCTestExpectation() - let itemMetadata = ItemMetadata(id: 2, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestFolder"), isPlaceholderItem: false, isCandidateForCacheCleanup: false) - let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata) + let itemMetadata = ItemMetadata(id: 2, name: "TestFolder", type: .folder, size: nil, parentID: 1, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false) + let taskRecord = try deletionTaskManagerMock.createTaskRecord(for: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let deletionTask = try deletionTaskManagerMock.getTask(for: taskRecord) let taskExecutor = DeletionTaskExecutor(provider: errorCloudProviderMock, itemMetadataManager: metadataManagerMock) taskExecutor.execute(task: deletionTask).then { diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/DownloadTaskExecutorTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/DownloadTaskExecutorTests.swift index 7a7fb3283..1a6f1f49f 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/DownloadTaskExecutorTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/DownloadTaskExecutorTests.swift @@ -19,10 +19,10 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { let cloudPath = CloudPath("/File 1") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let downloadTaskRecord = try DownloadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), replaceExisting: false, localURL: localURL) - let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) let taskExecutor = DownloadTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, downloadTaskManager: downloadTaskManagerMock) @@ -51,7 +51,7 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { let localURL = tmpDirectory.appendingPathComponent("itemNotFound.txt", isDirectory: false) let cloudPath = CloudPath("/itemNotFound.txt") - let itemMetadata = ItemMetadata(id: 3, name: "itemNotFound.txt", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 3, name: "itemNotFound.txt", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemMetadataResponse = { _ in Promise(CloudTaskTestError.correctPassthrough) @@ -61,7 +61,7 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { } let downloadTaskRecord = try DownloadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), replaceExisting: false, localURL: localURL) - let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) let taskExecutor = DownloadTaskExecutor(domainIdentifier: .test, provider: errorCloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, downloadTaskManager: downloadTaskManagerMock) @@ -89,10 +89,10 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { let existingLocalContentData = try Data(contentsOf: localURL) let cloudPath = CloudPath("/File 1") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let downloadTaskRecord = try DownloadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), replaceExisting: true, localURL: localURL) - let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let downloadTask = DownloadTask(taskRecord: downloadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) let taskExecutor = DownloadTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, downloadTaskManager: downloadTaskManagerMock) @@ -121,7 +121,7 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { func testDownloadPostProcessingForReplaceExisting() throws { let cloudPath = CloudPath("/File 1") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let localURL = tmpDirectory.appendingPathComponent("localItem.txt", isDirectory: false) let existingLocalContent = "Old Local FileContent" @@ -155,7 +155,7 @@ class DownloadTaskExecutorTests: CloudTaskExecutorTestCase { func testDownloadPostProcessingForNewFile() throws { let cloudPath = CloudPath("/File 1") let itemID: Int64 = 2 - let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: itemID, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let localURL = tmpDirectory.appendingPathComponent("localItem.txt", isDirectory: false) let downloadedContent = "Downloaded FileContent" diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/FolderCreationTaskExecutorTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/FolderCreationTaskExecutorTests.swift index 454dd3386..9cb40360d 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/FolderCreationTaskExecutorTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/FolderCreationTaskExecutorTests.swift @@ -16,9 +16,9 @@ class FolderCreationTaskExecutorTests: CloudTaskExecutorTestCase { func testCreateFolder() { let expectation = XCTestExpectation() let cloudPath = CloudPath("/NewFolder") - let itemMetadata = ItemMetadata(id: 2, name: "NewFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: 2, name: "NewFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) - let task = FolderCreationTask(itemMetadata: itemMetadata) + let task = FolderCreationTask(itemMetadata: itemMetadata, cloudPath: cloudPath) let taskExecutor = FolderCreationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock) taskExecutor.execute(task: task).then { item in XCTAssertEqual(1, self.metadataManagerMock.updatedMetadata.count) @@ -26,7 +26,7 @@ class FolderCreationTaskExecutorTests: CloudTaskExecutorTestCase { XCTAssertEqual(ItemStatus.isUploaded, item.metadata.statusCode) XCTAssertFalse(item.metadata.isPlaceholderItem) XCTAssertEqual(1, self.cloudProviderMock.createdFolders.count) - XCTAssertEqual(item.metadata.cloudPath.path, self.cloudProviderMock.createdFolders[0]) + XCTAssertEqual(cloudPath.path, self.cloudProviderMock.createdFolders[0]) }.catch { error in XCTFail("Promise failed with error: \(error)") }.always { @@ -38,7 +38,7 @@ class FolderCreationTaskExecutorTests: CloudTaskExecutorTestCase { func testCreateFolderFailWithSameErrorAsProvider() { let expectation = XCTestExpectation() let cloudPath = CloudPath("/NewFolder") - let itemMetadata = ItemMetadata(id: 2, name: "NewFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: 2, name: "NewFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.createFolderResponse = { folderPath in @@ -46,7 +46,7 @@ class FolderCreationTaskExecutorTests: CloudTaskExecutorTestCase { return Promise(CloudTaskTestError.correctPassthrough) } - let task = FolderCreationTask(itemMetadata: itemMetadata) + let task = FolderCreationTask(itemMetadata: itemMetadata, cloudPath: cloudPath) let taskExecutor = FolderCreationTaskExecutor(domainIdentifier: .test, provider: errorCloudProviderMock, itemMetadataManager: metadataManagerMock) taskExecutor.execute(task: task).then { _ in XCTFail("Promise should not fulfill if the provider fails with an error") diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift index ed4469068..46fc4925e 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/ItemEnumerationTaskTests.swift @@ -25,11 +25,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFileEnumeration() throws { let expectation = XCTestExpectation() let path = CloudPath("/File 1") - let fileMetadata = ItemMetadata(id: 2, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: path, isPlaceholderItem: false) + let fileMetadata = ItemMetadata(id: 2, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(fileMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(fileMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata, cloudPath: CloudPath("/").appendingPathComponent(fileMetadata.name)) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -46,7 +46,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { XCTAssertEqual(NSFileProviderItemIdentifier.rootContainerDatabaseValue, fetchedItemMetadata.parentID) XCTAssertNotNil(fetchedItemMetadata.lastModifiedDate) XCTAssertEqual(ItemStatus.isUploaded, fetchedItemMetadata.statusCode) - XCTAssertEqual(CloudPath("/File 1"), fetchedItemMetadata.cloudPath) + XCTAssertEqual("File 1", fetchedItemMetadata.name) XCTAssertFalse(fetchedItemMetadata.isPlaceholderItem) XCTAssertFalse(fetchedItemMetadata.isMaybeOutdated) @@ -69,7 +69,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let path = CloudPath("/File 1") let id: Int64 = 2 - let fileMetadata = ItemMetadata(id: id, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: path, isPlaceholderItem: false) + let fileMetadata = ItemMetadata(id: id, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(fileMetadata) uploadTaskManagerMock.getTaskRecordForClosure = { @@ -80,7 +80,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { } let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(fileMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata, cloudPath: CloudPath("/").appendingPathComponent(fileMetadata.name)) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -117,7 +117,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let path = CloudPath("/File 1") let id: Int64 = 2 - let fileMetadata = ItemMetadata(id: id, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: path, isPlaceholderItem: false) + let fileMetadata = ItemMetadata(id: id, name: "File 1", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(fileMetadata) let localURL = URL(fileURLWithPath: "/LocalFile 1") @@ -125,7 +125,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { try cachedFileManagerMock.cacheLocalFileInfo(for: id, localURL: localURL, lastModifiedDate: lastModifiedDate) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(fileMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: fileMetadata, cloudPath: CloudPath("/").appendingPathComponent(fileMetadata.name)) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -149,7 +149,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { XCTAssertEqual(NSFileProviderItemIdentifier.rootContainerDatabaseValue, fetchedItemMetadata.parentID) XCTAssertNotNil(fetchedItemMetadata.lastModifiedDate) XCTAssertEqual(ItemStatus.isUploaded, fetchedItemMetadata.statusCode) - XCTAssertEqual(CloudPath("/File 1"), fetchedItemMetadata.cloudPath) + XCTAssertEqual("File 1", fetchedItemMetadata.name) XCTAssertFalse(fetchedItemMetadata.isPlaceholderItem) XCTAssertFalse(fetchedItemMetadata.isMaybeOutdated) @@ -171,11 +171,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let cloudPath = CloudPath("/Test") let id: Int64 = 2 - let itemMetadata = ItemMetadata(id: id, name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(id: id, name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try metadataManagerMock.cacheMetadata(itemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: itemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemMetadataResponse = { _ in @@ -203,20 +203,20 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { // MARK: Folder func testFolderEnumeration() async throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) - let expectedItemMetadataInsideRootFolder = [ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Directory 1/"), isPlaceholderItem: false), - ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 1"), isPlaceholderItem: false), - ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 2"), isPlaceholderItem: false), - ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 3"), isPlaceholderItem: false), - ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 4"), isPlaceholderItem: false)] + let expectedItemMetadataInsideRootFolder = [ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), + ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), + ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), + ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), + ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false)] - let expectedItemMetadataInsideSubFolder = [ItemMetadata(id: 7, name: "Directory 2", type: .folder, size: 0, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Directory 1/Directory 2"), isPlaceholderItem: false), - ItemMetadata(id: 8, name: "File 5", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Directory 1/File 5"), isPlaceholderItem: false)] + let expectedItemMetadataInsideSubFolder = [ItemMetadata(id: 7, name: "Directory 2", type: .folder, size: 0, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), + ItemMetadata(id: 8, name: "File 5", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false)] let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) let permissionProviderMock = PermissionProviderMock() @@ -230,14 +230,14 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let rootFolderItemList = try await taskExecutor.execute(task: enumerationTask).getValue() XCTAssertEqual(5, rootFolderItemList.items.count) XCTAssertEqual(expectedRootFolderFileProviderItems, rootFolderItemList.items) - XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.metadata.cloudPath), rootFolderItemList.items.map(\.metadata.cloudPath)) + XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.metadata.name), rootFolderItemList.items.map(\.metadata.name)) XCTAssertEqual(6, self.metadataManagerMock.cachedMetadata.count) // Check cached metadata equals expected metadata, except the last modified date let expectedItemMetadata = [rootItemMetadata] + expectedItemMetadataInsideRootFolder XCTAssert(expectedItemMetadata.allSatisfy { expectedMetadata in self.metadataManagerMock.cachedMetadata.contains(where: { key, value in - key == expectedMetadata.id && value.name == expectedMetadata.name && value.type == expectedMetadata.type && value.size == expectedMetadata.size && value.parentID == expectedMetadata.parentID && value.statusCode == expectedMetadata.statusCode && value.cloudPath == expectedMetadata.cloudPath && value.isPlaceholderItem == expectedMetadata.isPlaceholderItem + key == expectedMetadata.id && value.name == expectedMetadata.name && value.type == expectedMetadata.type && value.size == expectedMetadata.size && value.parentID == expectedMetadata.parentID && value.statusCode == expectedMetadata.statusCode && value.parentID == expectedMetadata.parentID && value.isPlaceholderItem == expectedMetadata.isPlaceholderItem }) }) XCTAssertEqual(1, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) @@ -245,31 +245,31 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let subfolderMetadata = rootFolderItemList.items[0].metadata let subfolderEnumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(subfolderMetadata.id), pageToken: nil) - let subfolderEnumerationTask = ItemEnumerationTask(taskRecord: subfolderEnumerationTaskRecord, itemMetadata: subfolderMetadata) + let subfolderEnumerationTask = ItemEnumerationTask(taskRecord: subfolderEnumerationTaskRecord, itemMetadata: subfolderMetadata, cloudPath: CloudPath("/").appendingPathComponent(subfolderMetadata.name)) let subfolderItemList = try await taskExecutor.execute(task: subfolderEnumerationTask).getValue() XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) XCTAssert(self.itemEnumerationTaskManagerMock.removedTaskRecords.contains(where: { $0 == subfolderEnumerationTaskRecord })) XCTAssertEqual(2, subfolderItemList.items.count) XCTAssertEqual(expectedSubFolderFileProviderItems, subfolderItemList.items) - XCTAssertEqual(expectedSubFolderFileProviderItems.map(\.metadata.cloudPath), subfolderItemList.items.map(\.metadata.cloudPath)) + XCTAssertEqual(expectedSubFolderFileProviderItems.map(\.metadata.name), subfolderItemList.items.map(\.metadata.name)) XCTAssertEqual(8, self.metadataManagerMock.cachedMetadata.count) // Check cached metadata equals expected metadata, except the last modified date XCTAssert(expectedItemMetadataInsideSubFolder.allSatisfy { expectedMetadata in self.metadataManagerMock.cachedMetadata.contains(where: { key, value in - key == expectedMetadata.id && value.name == expectedMetadata.name && value.type == expectedMetadata.type && value.size == expectedMetadata.size && value.parentID == expectedMetadata.parentID && value.statusCode == expectedMetadata.statusCode && value.cloudPath == expectedMetadata.cloudPath && value.isPlaceholderItem == expectedMetadata.isPlaceholderItem + key == expectedMetadata.id && value.name == expectedMetadata.name && value.type == expectedMetadata.type && value.size == expectedMetadata.size && value.parentID == expectedMetadata.parentID && value.statusCode == expectedMetadata.statusCode && value.parentID == expectedMetadata.parentID && value.isPlaceholderItem == expectedMetadata.isPlaceholderItem }) }) }) } func testFolderEnumerationSameFolderTwice() async throws { - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let permissionProviderMock = PermissionProviderMock() let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -278,46 +278,46 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { $0.permissionProvider = permissionProviderMock }, operation: { permissionProviderMock.getPermissionsForAtReturnValue = .allowsReading - let expectedRootFolderFileProviderItems = [FileProviderItem(metadata: ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Directory 1/"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 1"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 2"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 3"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 4"), isPlaceholderItem: false), domainIdentifier: .test)] - let expectedChangedRootFolderFileProviderItems = [FileProviderItem(metadata: ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Directory 1/"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 2"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 3"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/File 4"), isPlaceholderItem: false), domainIdentifier: .test), - FileProviderItem(metadata: ItemMetadata(id: 7, name: "NewFileFromCloud", type: .file, size: 24, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/NewFileFromCloud"), isPlaceholderItem: false), domainIdentifier: .test)] + let expectedRootFolderFileProviderItems = [FileProviderItem(metadata: ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test)] + let expectedChangedRootFolderFileProviderItems = [FileProviderItem(metadata: ItemMetadata(id: 2, name: "Directory 1", type: .folder, size: 0, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 5, name: "File 3", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 6, name: "File 4", type: .file, size: 14, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test), + FileProviderItem(metadata: ItemMetadata(id: 7, name: "NewFileFromCloud", type: .file, size: 24, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test)] let firstEnumerationItemList = try await taskExecutor.execute(task: enumerationTask).getValue() XCTAssertEqual(5, firstEnumerationItemList.items.count) XCTAssertEqual(expectedRootFolderFileProviderItems, firstEnumerationItemList.items) - XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.metadata.cloudPath), firstEnumerationItemList.items.map(\.metadata.cloudPath)) + XCTAssertEqual(expectedRootFolderFileProviderItems.map(\.metadata.name), firstEnumerationItemList.items.map(\.metadata.name)) XCTAssertEqual(1, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) XCTAssert(self.itemEnumerationTaskManagerMock.removedTaskRecords.contains(where: { $0 == enumerationTaskRecord })) self.cloudProviderMock.files["/File 1"] = nil self.cloudProviderMock.files["/NewFileFromCloud"] = Data("NewFileFromCloud content".utf8) let secondEnumerationTaskRecord = ItemEnumerationTaskRecord(correspondingItem: rootItemMetadata.id!, pageToken: nil) - let secondEnumerationTask = ItemEnumerationTask(taskRecord: secondEnumerationTaskRecord, itemMetadata: rootItemMetadata) + let secondEnumerationTask = ItemEnumerationTask(taskRecord: secondEnumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let secondEnumerationItemList = try await taskExecutor.execute(task: secondEnumerationTask).getValue() XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) XCTAssert(self.itemEnumerationTaskManagerMock.removedTaskRecords.contains(where: { $0 == secondEnumerationTaskRecord })) XCTAssertEqual(5, secondEnumerationItemList.items.count) XCTAssertEqual(expectedChangedRootFolderFileProviderItems, secondEnumerationItemList.items) - XCTAssertEqual(expectedChangedRootFolderFileProviderItems.map(\.metadata.cloudPath), secondEnumerationItemList.items.map(\.metadata.cloudPath)) + XCTAssertEqual(expectedChangedRootFolderFileProviderItems.map(\.metadata.name), secondEnumerationItemList.items.map(\.metadata.name)) }) } func testFolderEnumerationPreservesUploadError() throws { let expectation = XCTestExpectation() let id: Int64 = 3 - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -336,7 +336,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { } }.then { _ -> Promise in let enumerationTaskRecord = ItemEnumerationTaskRecord(correspondingItem: rootItemMetadata.id!, pageToken: nil) - let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) return taskExecutor.execute(task: secondEnumerationTask) }.then { fileProviderItemList in XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) @@ -364,15 +364,15 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let paginatedMockedProvider = CloudProviderPaginationMock() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: paginatedMockedProvider, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) let id: Int64 = 2 - let itemMetadata = ItemMetadata(id: 2, name: "TestItem", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/TestItem"), isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 2, name: "TestItem", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cachedMetadata[XCTUnwrap(itemMetadata.id)] = itemMetadata taskExecutor.execute(task: enumerationTask).then { fileProviderItemList in @@ -396,15 +396,15 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let paginatedMockedProvider = CloudProviderPaginationMock() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) // Add some items to the ItemMetadataManager to simulate a previous folder enumeration on the root folder - try metadataManagerMock.cacheMetadata(ItemMetadata(id: 2, name: "OutdatedFile", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/OutdatedFile"), isPlaceholderItem: false, isCandidateForCacheCleanup: false)) - try metadataManagerMock.cacheMetadata(ItemMetadata(id: 3, name: "OutdatedFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/OutdatedFolder"), isPlaceholderItem: false, isCandidateForCacheCleanup: false)) + try metadataManagerMock.cacheMetadata(ItemMetadata(id: 2, name: "OutdatedFile", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false)) + try metadataManagerMock.cacheMetadata(ItemMetadata(id: 3, name: "OutdatedFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, isCandidateForCacheCleanup: false)) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: paginatedMockedProvider, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -423,7 +423,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { XCTAssert(self.itemEnumerationTaskManagerMock.removedTaskRecords.contains(where: { $0 == enumerationTaskRecord })) let enumerationTaskRecord = ItemEnumerationTaskRecord(correspondingItem: rootItemMetadata.id!, pageToken: nextPageToken) - let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) return taskExecutor.execute(task: secondEnumerationTask) }.then { fileProviderItemList in XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) @@ -448,11 +448,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationDidNotOverwriteReparentTask() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -464,14 +464,14 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { // Simulate a local rename, i.e. update name, status code and create a reparent task, which renames the item with filename File 1 to RenamedItem let item = fileProviderItemList.items[1] XCTAssertEqual("File 1", item.filename) - self.reparentTaskManagerMock.reparentTasks[item.metadata.id!] = ReparentTaskRecord(correspondingItem: item.metadata.id!, sourceCloudPath: item.metadata.cloudPath, targetCloudPath: newCloudPath, oldParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) + self.reparentTaskManagerMock.reparentTasks[item.metadata.id!] = ReparentTaskRecord(correspondingItem: item.metadata.id!, sourceCloudPath: CloudPath("/").appendingPathComponent(item.metadata.name), targetCloudPath: newCloudPath, oldParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, newParentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue) self.metadataManagerMock.cachedMetadata[item.metadata.id!]?.name = "RenamedItem" self.metadataManagerMock.cachedMetadata[item.metadata.id!]?.statusCode = .isUploading - self.metadataManagerMock.cachedMetadata[item.metadata.id!]?.cloudPath = newCloudPath + // Enumerate the Root folder again, File 1 has not been renamed in the cloud yet let enumerationTaskRecord = ItemEnumerationTaskRecord(correspondingItem: rootItemMetadata.id!, pageToken: nil) - let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) return taskExecutor.execute(task: secondEnumerationTask) }.then { fileProviderItemList in XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) @@ -482,7 +482,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { XCTAssertNil(oldItem) XCTAssertNotNil(renamedItem) XCTAssertEqual(ItemStatus.isUploading, renamedItem?.metadata.statusCode) - XCTAssertEqual(newCloudPath, renamedItem?.metadata.cloudPath) + XCTAssertEqual(newCloudPath.lastPathComponent, renamedItem?.metadata.name) }.catch { error in XCTFail("Error in promise: \(error)") }.always { @@ -494,11 +494,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationDidNotOverwriteDeletionTask() throws { let expectation = XCTestExpectation() - let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/"), isPlaceholderItem: false) + let rootItemMetadata = ItemMetadata(id: NSFileProviderItemIdentifier.rootContainerDatabaseValue, name: "Home", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(rootItemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(rootItemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) let taskExecutor = ItemEnumerationTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock, uploadTaskManager: uploadTaskManagerMock, reparentTaskManager: reparentTaskManagerMock, deletionTaskManager: deletionTaskManagerMock, itemEnumerationTaskManager: itemEnumerationTaskManagerMock, deleteItemHelper: deleteItemHelper) @@ -511,12 +511,12 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { // Simulate a local item deletion, i.e. update the status code and create a deletion task. let item = fileProviderItemList.items[1] XCTAssertEqual("File 1", item.filename) - self.deletionTaskManagerMock.deletionTasks[item.metadata.id!] = DeletionTaskRecord(correspondingItem: item.metadata.id!, cloudPath: item.metadata.cloudPath, parentID: item.metadata.parentID, itemType: item.metadata.type) + self.deletionTaskManagerMock.deletionTasks[item.metadata.id!] = DeletionTaskRecord(correspondingItem: item.metadata.id!, cloudPath: CloudPath("/").appendingPathComponent(item.metadata.name), parentID: item.metadata.parentID, itemType: item.metadata.type) self.metadataManagerMock.cachedMetadata[item.metadata.id!]?.statusCode = .isUploading // Enumerate the Root folder again, File 1 has not been deleted in the cloud yet let enumerationTaskRecord = ItemEnumerationTaskRecord(correspondingItem: rootItemMetadata.id!, pageToken: nil) - let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata) + let secondEnumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: rootItemMetadata, cloudPath: CloudPath("/")) return taskExecutor.execute(task: secondEnumerationTask) }.then { fileProviderItemList in XCTAssertEqual(2, self.itemEnumerationTaskManagerMock.removedTaskRecords.count) @@ -535,16 +535,16 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationOfflineFallbackWithCachedItems() throws { let expectation = XCTestExpectation() - let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder"), isPlaceholderItem: false, lastEnumeratedAt: Date()) + let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, lastEnumeratedAt: Date()) try metadataManagerMock.cacheMetadata(folderMetadata) - let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder/File 1"), isPlaceholderItem: false) - let child2 = ItemMetadata(id: 4, name: "Directory 1", type: .folder, size: 0, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder/Directory 1/"), isPlaceholderItem: false) + let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) + let child2 = ItemMetadata(id: 4, name: "Directory 1", type: .folder, size: 0, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(child1) try metadataManagerMock.cacheMetadata(child2) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(folderMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in @@ -566,11 +566,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationOfflineFallbackWithNoCachedItems() throws { let expectation = XCTestExpectation() - let folderMetadata = ItemMetadata(id: 2, name: "EmptyFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/EmptyFolder"), isPlaceholderItem: false) + let folderMetadata = ItemMetadata(id: 2, name: "EmptyFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(folderMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(folderMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in @@ -594,11 +594,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationOfflineFallbackWithEmptyEnumeratedFolder() throws { let expectation = XCTestExpectation() - let folderMetadata = ItemMetadata(id: 2, name: "EmptyFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/EmptyFolder"), isPlaceholderItem: false, lastEnumeratedAt: Date()) + let folderMetadata = ItemMetadata(id: 2, name: "EmptyFolder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, lastEnumeratedAt: Date()) try metadataManagerMock.cacheMetadata(folderMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(folderMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in @@ -620,14 +620,14 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationOfflineFallbackWithPageToken() throws { let expectation = XCTestExpectation() - let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder"), isPlaceholderItem: false) + let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(folderMetadata) - let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder/File 1"), isPlaceholderItem: false) + let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(child1) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(folderMetadata.id), pageToken: "somePageToken") - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in @@ -651,12 +651,12 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { func testFolderEnumerationOfflineFallbackPreservesCachedFileInfo() throws { let expectation = XCTestExpectation() - let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder"), isPlaceholderItem: false, lastEnumeratedAt: Date()) + let folderMetadata = ItemMetadata(id: 2, name: "Folder", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false, lastEnumeratedAt: Date()) try metadataManagerMock.cacheMetadata(folderMetadata) let lastModifiedDate = Date(timeIntervalSince1970: 0) - let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: lastModifiedDate, statusCode: .isUploaded, cloudPath: CloudPath("/Folder/File 1"), isPlaceholderItem: false) - let child2 = ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Folder/File 2"), isPlaceholderItem: false) + let child1 = ItemMetadata(id: 3, name: "File 1", type: .file, size: 14, parentID: 2, lastModifiedDate: lastModifiedDate, statusCode: .isUploaded, isPlaceholderItem: false) + let child2 = ItemMetadata(id: 4, name: "File 2", type: .file, size: 14, parentID: 2, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(child1) try metadataManagerMock.cacheMetadata(child2) @@ -664,7 +664,7 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { try cachedFileManagerMock.cacheLocalFileInfo(for: 3, localURL: localURL, lastModifiedDate: lastModifiedDate) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(folderMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: folderMetadata, cloudPath: CloudPath("/").appendingPathComponent(folderMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in @@ -700,11 +700,11 @@ class ItemEnumerationTaskTests: CloudTaskExecutorTestCase { let expectation = XCTestExpectation() let cloudPath = CloudPath("/Test") let id: Int64 = 2 - let itemMetadata = ItemMetadata(id: id, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true) + let itemMetadata = ItemMetadata(id: id, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true) try metadataManagerMock.cacheMetadata(itemMetadata) let enumerationTaskRecord = try ItemEnumerationTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), pageToken: nil) - let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: itemMetadata) + let enumerationTask = ItemEnumerationTask(taskRecord: enumerationTaskRecord, itemMetadata: itemMetadata, cloudPath: CloudPath("/").appendingPathComponent(itemMetadata.name)) let errorCloudProviderMock = CloudProviderErrorMock() errorCloudProviderMock.fetchItemListResponse = { _, _ in diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/ReparentTaskExecutorTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/ReparentTaskExecutorTests.swift index 151251160..e1e2d58e2 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/ReparentTaskExecutorTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/ReparentTaskExecutorTests.swift @@ -17,11 +17,11 @@ class ReparentTaskExecutorTests: CloudTaskExecutorTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Folder/Test.txt") - let itemMetadata = ItemMetadata(id: 2, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 2, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let reparentTaskRecord = try ReparentTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: itemMetadata.parentID) - let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata) + let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata, cloudPath: sourceCloudPath) let taskExecutor = ReparentTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, reparentTaskManager: reparentTaskManagerMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock) taskExecutor.execute(task: reparentTask).then { item in @@ -49,11 +49,11 @@ class ReparentTaskExecutorTests: CloudTaskExecutorTestCase { let sourceCloudPath = CloudPath("/Test") let targetCloudPath = CloudPath("/Folder/Test") - let itemMetadata = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let reparentTaskRecord = try ReparentTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: itemMetadata.parentID) - let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata) + let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata, cloudPath: sourceCloudPath) let taskExecutor = ReparentTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, reparentTaskManager: reparentTaskManagerMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock) taskExecutor.execute(task: reparentTask).then { item in @@ -86,11 +86,11 @@ class ReparentTaskExecutorTests: CloudTaskExecutorTestCase { let sourceCloudPath = CloudPath("/Test.txt") let targetCloudPath = CloudPath("/Folder/Test.txt") - let itemMetadata = ItemMetadata(id: 2, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 2, name: "Test.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let reparentTaskRecord = try ReparentTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: itemMetadata.parentID) - let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata) + let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata, cloudPath: sourceCloudPath) let taskExecutor = ReparentTaskExecutor(domainIdentifier: .test, provider: errorCloudProviderMock, reparentTaskManager: reparentTaskManagerMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock) taskExecutor.execute(task: reparentTask).then { _ in XCTFail("Promise should not fulfill if the provider fails with an error") @@ -116,11 +116,11 @@ class ReparentTaskExecutorTests: CloudTaskExecutorTestCase { let sourceCloudPath = CloudPath("/Test") let targetCloudPath = CloudPath("/Folder/Test") - let itemMetadata = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: sourceCloudPath, isPlaceholderItem: false) + let itemMetadata = ItemMetadata(id: 2, name: "Test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) try metadataManagerMock.cacheMetadata(itemMetadata) let reparentTaskRecord = try ReparentTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), sourceCloudPath: sourceCloudPath, targetCloudPath: targetCloudPath, oldParentID: itemMetadata.parentID, newParentID: itemMetadata.parentID) - let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata) + let reparentTask = ReparentTask(taskRecord: reparentTaskRecord, itemMetadata: itemMetadata, cloudPath: sourceCloudPath) let taskExecutor = ReparentTaskExecutor(domainIdentifier: .test, provider: errorCloudProviderMock, reparentTaskManager: reparentTaskManagerMock, itemMetadataManager: metadataManagerMock, cachedFileManager: cachedFileManagerMock) taskExecutor.execute(task: reparentTask).then { _ in XCTFail("Promise should not fulfill if the provider fails with an error") diff --git a/CryptomatorFileProviderTests/Middleware/TaskExecutor/UploadTaskExecutorTests.swift b/CryptomatorFileProviderTests/Middleware/TaskExecutor/UploadTaskExecutorTests.swift index 8710e8452..f16ba2cd4 100644 --- a/CryptomatorFileProviderTests/Middleware/TaskExecutor/UploadTaskExecutorTests.swift +++ b/CryptomatorFileProviderTests/Middleware/TaskExecutor/UploadTaskExecutorTests.swift @@ -19,15 +19,15 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { try "TestContent".write(to: localURL, atomically: true, encoding: .utf8) let cloudPath = CloudPath("/FileToBeUploaded") let progressManagerMock = ProgressManagerMock() - let itemMetadata = ItemMetadata(id: itemID, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: itemID, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) cachedFileManagerMock.cachedLocalFileInfo[itemID] = LocalCachedFileInfo(lastModifiedDate: nil, correspondingItem: itemID, localLastModifiedDate: Date(), localURL: localURL) let uploadTaskExecutor = UploadTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, cachedFileManager: cachedFileManagerMock, itemMetadataManager: metadataManagerMock, uploadTaskManager: uploadTaskManagerMock, progressManager: progressManagerMock) let mockedCloudDate = Date(timeIntervalSinceReferenceDate: 0) - cloudProviderMock.lastModifiedDate[itemMetadata.cloudPath.path] = mockedCloudDate + cloudProviderMock.lastModifiedDate[cloudPath.path] = mockedCloudDate let uploadTaskRecord = UploadTaskRecord(correspondingItem: itemID, lastFailedUploadDate: nil, uploadErrorCode: nil, uploadErrorDomain: nil, uploadStartedAt: nil) - let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) uploadTaskExecutor.execute(task: uploadTask).then { _ in XCTAssertEqual(Data("TestContent".utf8), self.cloudProviderMock.createdFiles["/FileToBeUploaded"]) @@ -39,7 +39,7 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { let cachedFileInfo = self.cachedFileManagerMock.cachedLocalFileInfo[itemID] let lastModifiedDate = cachedFileInfo?.lastModifiedDate XCTAssertNotNil(lastModifiedDate) - XCTAssertEqual(self.cloudProviderMock.lastModifiedDate[itemMetadata.cloudPath.path], lastModifiedDate) + XCTAssertEqual(self.cloudProviderMock.lastModifiedDate[cloudPath.path], lastModifiedDate) // Verify that the upload task has been removed XCTAssertEqual([itemMetadata.id], self.uploadTaskManagerMock.removeTaskRecordForReceivedInvocations) @@ -61,15 +61,15 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { let localURL = tmpDirectory.appendingPathComponent("FileToBeUploaded", isDirectory: false) try "TestContent".write(to: localURL, atomically: true, encoding: .utf8) let cloudPath = CloudPath("/FileToBeUploaded") - let itemMetadata = ItemMetadata(id: 2, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: 2, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) let mockedCloudDate = Date(timeIntervalSinceReferenceDate: 0) - cloudProviderMock.lastModifiedDate[itemMetadata.cloudPath.path] = mockedCloudDate + cloudProviderMock.lastModifiedDate[cloudPath.path] = mockedCloudDate let uploadTaskExecutor = UploadTaskExecutor(domainIdentifier: .test, provider: cloudProviderMock, cachedFileManager: cachedFileManagerMock, itemMetadataManager: metadataManagerMock, uploadTaskManager: uploadTaskManagerMock) let uploadTaskRecord = try UploadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), lastFailedUploadDate: nil, uploadErrorCode: nil, uploadErrorDomain: nil, uploadStartedAt: nil) - let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) uploadTaskExecutor.execute(task: uploadTask).then { _ in XCTFail("Promise should not fulfill for missing local cached file info") @@ -90,17 +90,17 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { let localURL = tmpDirectory.appendingPathComponent("FileToBeUploaded", isDirectory: false) try "TestContent".write(to: localURL, atomically: true, encoding: .utf8) let cloudPath = CloudPath("/FileToBeUploaded") - let itemMetadata = ItemMetadata(id: 2, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: 2, name: "FileToBeUploaded", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) cachedFileManagerMock.cachedLocalFileInfo[2] = LocalCachedFileInfo(lastModifiedDate: nil, correspondingItem: 2, localLastModifiedDate: Date(), localURL: localURL) let cloudProviderUploadInconsistencyMock = CloudProviderUploadInconsistencyMock() let mockedCloudDate = Date(timeIntervalSinceReferenceDate: 0) - cloudProviderUploadInconsistencyMock.lastModifiedDate[itemMetadata.cloudPath.path] = mockedCloudDate + cloudProviderUploadInconsistencyMock.lastModifiedDate[cloudPath.path] = mockedCloudDate let uploadTaskExecutor = UploadTaskExecutor(domainIdentifier: .test, provider: cloudProviderUploadInconsistencyMock, cachedFileManager: cachedFileManagerMock, itemMetadataManager: metadataManagerMock, uploadTaskManager: uploadTaskManagerMock) let uploadTaskRecord = try UploadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), lastFailedUploadDate: nil, uploadErrorCode: nil, uploadErrorDomain: nil, uploadStartedAt: nil) - let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) uploadTaskExecutor.execute(task: uploadTask).then { item in // Verify that the file has been modified since the upload began. @@ -133,7 +133,7 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { let localURL = tmpDirectory.appendingPathComponent("itemNotFound.txt", isDirectory: false) try "".write(to: localURL, atomically: true, encoding: .utf8) let cloudPath = CloudPath("/itemNotFound.txt") - let itemMetadata = ItemMetadata(id: 2, name: "itemNotFound.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: true, isCandidateForCacheCleanup: false) + let itemMetadata = ItemMetadata(id: 2, name: "itemNotFound.txt", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: true, isCandidateForCacheCleanup: false) cachedFileManagerMock.cachedLocalFileInfo[2] = LocalCachedFileInfo(lastModifiedDate: nil, correspondingItem: 2, localLastModifiedDate: Date(), localURL: localURL) let errorCloudProviderMock = CloudProviderErrorMock() @@ -144,7 +144,7 @@ class UploadTaskExecutorTests: CloudTaskExecutorTestCase { let uploadTaskExecutor = UploadTaskExecutor(domainIdentifier: .test, provider: errorCloudProviderMock, cachedFileManager: cachedFileManagerMock, itemMetadataManager: metadataManagerMock, uploadTaskManager: uploadTaskManagerMock) let uploadTaskRecord = try UploadTaskRecord(correspondingItem: XCTUnwrap(itemMetadata.id), lastFailedUploadDate: nil, uploadErrorCode: nil, uploadErrorDomain: nil, uploadStartedAt: nil) - let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, onURLSessionTaskCreation: nil) + let uploadTask = UploadTask(taskRecord: uploadTaskRecord, itemMetadata: itemMetadata, cloudPath: cloudPath, onURLSessionTaskCreation: nil) let promise = uploadTaskExecutor.execute(task: uploadTask) wait(for: promise) diff --git a/CryptomatorFileProviderTests/PermissionProviderImplTests.swift b/CryptomatorFileProviderTests/PermissionProviderImplTests.swift index 8416bccc4..83a974a89 100644 --- a/CryptomatorFileProviderTests/PermissionProviderImplTests.swift +++ b/CryptomatorFileProviderTests/PermissionProviderImplTests.swift @@ -32,7 +32,7 @@ final class PermissionProviderImplTests: XCTestCase { self.fullVersionCheckerMock.isFullVersion = true let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsReading, actualCapabilities) } @@ -46,7 +46,7 @@ final class PermissionProviderImplTests: XCTestCase { self.fullVersionCheckerMock.isFullVersion = true let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(Self.defaultFolderCapabilities, actualCapabilities) } @@ -60,7 +60,7 @@ final class PermissionProviderImplTests: XCTestCase { self.fullVersionCheckerMock.isFullVersion = false let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsReading, actualCapabilities) } @@ -74,7 +74,7 @@ final class PermissionProviderImplTests: XCTestCase { self.fullVersionCheckerMock.isFullVersion = false let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsDeleting, actualCapabilities) } @@ -88,7 +88,7 @@ final class PermissionProviderImplTests: XCTestCase { self.fullVersionCheckerMock.isFullVersion = false let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .uploadError, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsDeleting, actualCapabilities) } @@ -103,7 +103,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .inactive) let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual([.allowsWriting, .allowsReading, .allowsDeleting, .allowsRenaming, .allowsReparenting], actualCapabilities) } @@ -120,7 +120,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .active) let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsReading, actualCapabilities) } @@ -135,7 +135,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .inactive) let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(NSFileProviderItemCapabilities.allowsReading, actualCapabilities) } @@ -150,7 +150,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .active) let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .folder, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(Self.defaultFolderCapabilities, actualCapabilities) } @@ -165,7 +165,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .active) let cloudPath = CloudPath("/test") - let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test", type: .folder, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploading, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual(Self.defaultFolderCapabilities, actualCapabilities) } @@ -180,7 +180,7 @@ final class PermissionProviderImplTests: XCTestCase { self.hubRepositoryMock.getHubVaultVaultIDReturnValue = .init(vaultUID: "12345", subscriptionState: .active) let cloudPath = CloudPath("/test.txt") - let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: cloudPath, isPlaceholderItem: false) + let metadata = ItemMetadata(id: 2, name: "test.txt", type: .file, size: 100, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false) let actualCapabilities = PermissionProviderImpl().getPermissions(for: metadata, at: .test) XCTAssertEqual([.allowsWriting, .allowsReading, .allowsDeleting, .allowsRenaming, .allowsReparenting], actualCapabilities) } diff --git a/CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift b/CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift index 94e0c2b0a..9fcd6a05c 100644 --- a/CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift +++ b/CryptomatorFileProviderTests/ServiceSource/CacheManagingServiceSourceTests.swift @@ -67,7 +67,7 @@ class CacheManagingServiceSourceTests: XCTestCase { $0.permissionProvider = permissionProviderMock } operation: { permissionProviderMock.getPermissionsForAtReturnValue = .allowsReading - testItem = FileProviderItem(metadata: ItemMetadata(id: itemID, name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, cloudPath: CloudPath("/Test"), isPlaceholderItem: false), domainIdentifier: .test) + testItem = FileProviderItem(metadata: ItemMetadata(id: itemID, name: "Test", type: .file, size: nil, parentID: NSFileProviderItemIdentifier.rootContainerDatabaseValue, lastModifiedDate: nil, statusCode: .isUploaded, isPlaceholderItem: false), domainIdentifier: .test) serviceSource.getItem = { receivedItemIdentifier in guard itemIdentifier == receivedItemIdentifier else { return nil diff --git a/CryptomatorFileProviderTests/WorkingSetObserverTests.swift b/CryptomatorFileProviderTests/WorkingSetObserverTests.swift index b4c6951af..fbe33f15c 100644 --- a/CryptomatorFileProviderTests/WorkingSetObserverTests.swift +++ b/CryptomatorFileProviderTests/WorkingSetObserverTests.swift @@ -17,7 +17,7 @@ class WorkingSetObserverTests: XCTestCase { var notificatorMock: FileProviderNotificatorTypeMock! let updatedMetadataIDs: [Int64] = [1, 2, 3] lazy var updatedItems: [FileProviderItem] = updatedMetadataIDs.map { - FileProviderItem(metadata: ItemMetadata(id: $0, name: "\($0)", type: .file, size: nil, parentID: 0, lastModifiedDate: nil, statusCode: .isDownloading, cloudPath: CloudPath("/\($0)"), isPlaceholderItem: false), domainIdentifier: .test) + FileProviderItem(metadata: ItemMetadata(id: $0, name: "\($0)", type: .file, size: nil, parentID: 0, lastModifiedDate: nil, statusCode: .isDownloading, isPlaceholderItem: false), domainIdentifier: .test) } override func setUpWithError() throws {