@@ -2022,4 +2022,143 @@ final class EnumeratorTests: NextcloudFileProviderKitTestCase {
20222022 )
20232023 }
20242024 }
2025+
2026+ func testLockChangeInNestedFileDetectedDuringWorkingSetEnumeration( ) async throws {
2027+ let db = Self . dbManager. ncDatabase ( )
2028+ debugPrint ( db)
2029+
2030+ let remoteChildFolder = MockRemoteItem (
2031+ identifier: " childFolder " ,
2032+ versionIdentifier: " CHILD_V1 " ,
2033+ name: " childFolder " ,
2034+ remotePath: Self . account. davFilesUrl + " /folder/childFolder " ,
2035+ directory: true ,
2036+ account: Self . account. ncKitAccount,
2037+ username: Self . account. username,
2038+ userId: Self . account. id,
2039+ serverUrl: Self . account. serverUrl
2040+ )
2041+
2042+ let remoteNestedFile = MockRemoteItem (
2043+ identifier: " nestedFile " ,
2044+ versionIdentifier: " FILE_V1 " ,
2045+ name: " nestedFile.txt " ,
2046+ remotePath: Self . account. davFilesUrl + " /folder/childFolder/nestedFile.txt " ,
2047+ locked: true ,
2048+ lockOwner: " otherUser " ,
2049+ lockTimeOut: Date . now. advanced ( by: 1_000_000 ) ,
2050+ account: Self . account. ncKitAccount,
2051+ username: Self . account. username,
2052+ userId: Self . account. id,
2053+ serverUrl: Self . account. serverUrl
2054+ )
2055+
2056+ remoteFolder. children = [ remoteChildFolder]
2057+ remoteChildFolder. parent = remoteFolder
2058+ remoteChildFolder. children = [ remoteNestedFile]
2059+ remoteNestedFile. parent = remoteChildFolder
2060+
2061+ let remoteInterface = MockRemoteInterface ( account: Self . account, rootItem: rootItem)
2062+
2063+ var folderMetadata = remoteFolder. toItemMetadata ( account: Self . account)
2064+ folderMetadata. visitedDirectory = true
2065+ Self . dbManager. addItemMetadata ( folderMetadata)
2066+
2067+ var childFolderMetadata = remoteChildFolder. toItemMetadata ( account: Self . account)
2068+ childFolderMetadata. visitedDirectory = true
2069+ Self . dbManager. addItemMetadata ( childFolderMetadata)
2070+
2071+ var nestedFileMetadata = remoteNestedFile. toItemMetadata ( account: Self . account)
2072+ nestedFileMetadata. downloaded = true
2073+ nestedFileMetadata. lock = false
2074+ nestedFileMetadata. lockOwner = " "
2075+ nestedFileMetadata. lockTimeOut = nil
2076+ Self . dbManager. addItemMetadata ( nestedFileMetadata)
2077+
2078+ let anchorDate = Date ( ) . addingTimeInterval ( - 300 )
2079+ let formatter = ISO8601DateFormatter ( )
2080+ let anchor = try NSFileProviderSyncAnchor ( XCTUnwrap ( formatter. string ( from: anchorDate) . data ( using: . utf8) ) )
2081+
2082+ let enumerator = try Enumerator (
2083+ enumeratedItemIdentifier: . workingSet,
2084+ account: Self . account,
2085+ remoteInterface: remoteInterface,
2086+ dbManager: Self . dbManager,
2087+ log: FileProviderLogMock ( )
2088+ )
2089+
2090+ let observer = MockChangeObserver ( enumerator: enumerator)
2091+ try await observer. enumerateChanges ( from: anchor)
2092+
2093+ XCTAssertNil ( observer. error)
2094+
2095+ let dbNestedFile = try XCTUnwrap (
2096+ Self . dbManager. itemMetadata ( ocId: remoteNestedFile. identifier)
2097+ )
2098+ XCTAssertTrue (
2099+ dbNestedFile. lock,
2100+ " Lock change on nested file should be detected during working set enumeration "
2101+ )
2102+ XCTAssertEqual ( dbNestedFile. lockOwner, " otherUser " )
2103+
2104+ let nestedFileReported = observer. changedItems. contains {
2105+ $0. itemIdentifier. rawValue == remoteNestedFile. identifier
2106+ }
2107+ XCTAssertTrue (
2108+ nestedFileReported,
2109+ " Nested file with lock change should be reported as changed "
2110+ )
2111+ }
2112+
2113+ func testLockTokenPreservedDuringTargetDepthRead( ) async throws {
2114+ let db = Self . dbManager. ncDatabase ( )
2115+ debugPrint ( db)
2116+
2117+ let remoteFile = MockRemoteItem (
2118+ identifier: " lockedFile " ,
2119+ versionIdentifier: " V1 " ,
2120+ name: " lockedFile.txt " ,
2121+ remotePath: Self . account. davFilesUrl + " /lockedFile.txt " ,
2122+ locked: true ,
2123+ lockOwner: Self . account. username,
2124+ lockTimeOut: Date . now. advanced ( by: 1_000_000 ) ,
2125+ account: Self . account. ncKitAccount,
2126+ username: Self . account. username,
2127+ userId: Self . account. id,
2128+ serverUrl: Self . account. serverUrl
2129+ )
2130+
2131+ rootItem. children = [ remoteFile]
2132+ remoteFile. parent = rootItem
2133+
2134+ let remoteInterface = MockRemoteInterface ( account: Self . account, rootItem: rootItem)
2135+
2136+ var fileMetadata = remoteFile. toItemMetadata ( account: Self . account)
2137+ fileMetadata. lockToken = " local-lock-token-123 "
2138+ fileMetadata. downloaded = true
2139+ Self . dbManager. addItemMetadata ( fileMetadata)
2140+
2141+ let preRead = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " lockedFile " ) )
2142+ XCTAssertEqual ( preRead. lockToken, " local-lock-token-123 " )
2143+
2144+ let ( metadatas, _, _, _, _, readError) = await Enumerator . readServerUrl (
2145+ Self . account. davFilesUrl + " /lockedFile.txt " ,
2146+ account: Self . account,
2147+ remoteInterface: remoteInterface,
2148+ dbManager: Self . dbManager,
2149+ depth: . target,
2150+ log: FileProviderLogMock ( )
2151+ )
2152+
2153+ XCTAssertNil ( readError)
2154+ XCTAssertNotNil ( metadatas)
2155+
2156+ let postRead = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " lockedFile " ) )
2157+ XCTAssertEqual (
2158+ postRead. lockToken,
2159+ " local-lock-token-123 " ,
2160+ " lockToken must be preserved across target-depth reads "
2161+ )
2162+ XCTAssertTrue ( postRead. downloaded, " downloaded state must be preserved " )
2163+ }
20252164}
0 commit comments