Files
2025-12-03 16:48:23 +01:00

149 lines
6.7 KiB
Swift

// SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: LGPL-3.0-or-later
@preconcurrency import FileProvider
import Foundation
import NextcloudFileProviderKit
import NextcloudFileProviderKitMocks
import NextcloudKit
import RealmSwift
import TestInterface
import XCTest
final class MaterialisedEnumerationObserverTests: NextcloudFileProviderKitTestCase {
static let account = Account(
user: "testUser", id: "testUserId", serverUrl: "https://mock.nc.com", password: "abcd"
)
override func setUp() {
super.setUp()
Realm.Configuration.defaultConfiguration.inMemoryIdentifier = name
}
func testMaterialisedObserverWithNoPreexistingState() async {
let dbManager = FilesDatabaseManager(account: Self.account, databaseDirectory: makeDatabaseDirectory(), fileProviderDomainIdentifier: NSFileProviderDomainIdentifier("test"), log: FileProviderLogMock())
// The database is intentionally left empty.
let remoteInterface = MockRemoteInterface(account: Self.account)
let enumeratedFile =
SendableItemMetadata(ocId: "file1", fileName: "file1.txt", account: Self.account)
var enumeratedDir =
SendableItemMetadata(ocId: "dir1", fileName: "dir1", account: Self.account)
enumeratedDir.directory = true
let expect = XCTestExpectation(description: "Enumerator completion handler called")
// The observer's logic requires metadata to exist in the DB to update it.
let observer = MaterializedEnumerationObserver(account: Self.account, dbManager: dbManager, log: FileProviderLogMock()) { newlyMaterialisedIds, unmaterialisedIds in
XCTAssertTrue(
unmaterialisedIds.isEmpty,
"Unmaterialised set should be empty when DB starts empty."
)
// The items are correctly identified as newly materialized because they weren't in the
// DB's materialized list (which was empty).
XCTAssertEqual(
newlyMaterialisedIds.count,
2,
"Both enumerated items should be identified as newly materialised."
)
XCTAssertTrue(newlyMaterialisedIds.contains(NSFileProviderItemIdentifier("file1")))
XCTAssertTrue(newlyMaterialisedIds.contains(NSFileProviderItemIdentifier("dir1")))
// Verify that the database state is NOT updated
let fileMetadata = dbManager.itemMetadata(ocId: "file1")
XCTAssertNil(
fileMetadata,
"Metadata should NOT be in the DB, as the observer does not add missing items."
)
let dirMetadata = dbManager.itemMetadata(ocId: "dir1")
XCTAssertNil(
dirMetadata,
"Metadata should NOT be in the DB, as the observer does not add missing items."
)
expect.fulfill()
}
let enumerator = MockEnumerator(
account: Self.account, dbManager: dbManager, remoteInterface: remoteInterface
)
enumerator.enumeratorItems = [enumeratedFile, enumeratedDir]
enumerator.enumerateItems(for: observer, startingAt: NSFileProviderPage(Data(count: 1)))
await fulfillment(of: [expect], timeout: 1)
}
func testMaterialisedObserverWithMixedState() async {
// Setup a DB with a mix of materialized and non-materialised items.
var itemA = SendableItemMetadata(ocId: "itemA", fileName: "itemA", account: Self.account)
itemA.downloaded = true // Was materialised
var itemB = SendableItemMetadata(ocId: "itemB", fileName: "itemB", account: Self.account)
itemB.downloaded = false // Was NOT materialised
var itemC = SendableItemMetadata(ocId: "itemC", fileName: "itemC", account: Self.account)
itemC.downloaded = true // Was materialised
var dirD = SendableItemMetadata(ocId: "dirD", fileName: "dirD", account: Self.account)
dirD.directory = true
dirD.visitedDirectory = true // Was materialised
let dbManager = FilesDatabaseManager(account: Self.account, databaseDirectory: makeDatabaseDirectory(), fileProviderDomainIdentifier: NSFileProviderDomainIdentifier("test"), log: FileProviderLogMock())
dbManager.addItemMetadata(itemA)
dbManager.addItemMetadata(itemB)
dbManager.addItemMetadata(itemC)
dbManager.addItemMetadata(dirD)
let remoteInterface = MockRemoteInterface(account: Self.account)
let expect = XCTestExpectation(description: "Enumerator completion handler called")
let enumeratorItemsToReturn = [itemB, itemC]
let observer = MaterializedEnumerationObserver(account: Self.account, dbManager: dbManager, log: FileProviderLogMock()) { newlyMaterialisedIds, unmaterialisedIds in
// Unmaterialised: itemA and dirD were materialized but not in the latest enumeration.
XCTAssertEqual(
unmaterialisedIds.count, 2, "itemA and dirD should be reported as unmaterialised."
)
XCTAssertTrue(unmaterialisedIds.contains(NSFileProviderItemIdentifier("itemA")))
XCTAssertTrue(unmaterialisedIds.contains(NSFileProviderItemIdentifier("dirD")))
// Newly Materialised: itemB was NOT materialized but WAS in the latest enumeration.
XCTAssertEqual(
newlyMaterialisedIds.count, 1, "itemB should be reported as newly materialised."
)
XCTAssertEqual(newlyMaterialisedIds.first, NSFileProviderItemIdentifier("itemB"))
// Check final database state
let finalItemA = dbManager.itemMetadata(ocId: "itemA")
XCTAssertFalse(
finalItemA?.downloaded ?? true, "itemA should now be marked as not downloaded."
)
let finalItemB = dbManager.itemMetadata(ocId: "itemB")
XCTAssertTrue(
finalItemB?.downloaded ?? false, "itemB should now be marked as downloaded."
)
let finalItemC = dbManager.itemMetadata(ocId: "itemC")
XCTAssertTrue(finalItemC?.downloaded ?? false, "itemC should remain downloaded.")
let finalDirD = dbManager.itemMetadata(ocId: "dirD")
XCTAssertFalse(
finalDirD?.visitedDirectory ?? true, "dirD should now be marked as not visited."
)
expect.fulfill()
}
let enumerator = MockEnumerator(
account: Self.account, dbManager: dbManager, remoteInterface: remoteInterface
)
enumerator.enumeratorItems = enumeratorItemsToReturn
enumerator.enumerateItems(for: observer, startingAt: NSFileProviderPage(Data(count: 1)))
await fulfillment(of: [expect], timeout: 1)
}
}