@@ -1073,6 +1073,211 @@ final class FilesDatabaseManagerTests: XCTestCase {
10731073 XCTAssertFalse ( finalMetadata. keepDownloaded)
10741074 }
10751075
1076+ func testKeepDownloadedRetainedDuringDepth1ReadUpdate( ) throws {
1077+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1078+
1079+ // Create initial metadata with keepDownloaded = true
1080+ var initialMetadata = SendableItemMetadata ( ocId: " test-keep-downloaded " , fileName: " test.txt " , account: account)
1081+ initialMetadata. downloaded = true
1082+ initialMetadata. uploaded = true
1083+ initialMetadata. keepDownloaded = true
1084+ initialMetadata. etag = " old-etag "
1085+
1086+ Self . dbManager. addItemMetadata ( initialMetadata)
1087+
1088+ // Verify initial state
1089+ let storedMetadata = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " test-keep-downloaded " ) )
1090+ XCTAssertTrue ( storedMetadata. keepDownloaded, " Initial keepDownloaded should be true " )
1091+ XCTAssertTrue ( storedMetadata. downloaded, " Initial downloaded should be true " )
1092+
1093+ // Update metadata with new etag (simulating server update)
1094+ var updatedMetadata = initialMetadata
1095+ updatedMetadata. etag = " new-etag "
1096+ updatedMetadata. keepDownloaded = false // This would be the case when converting from NKFile
1097+
1098+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1099+ account: account. ncKitAccount,
1100+ serverUrl: account. davFilesUrl,
1101+ updatedMetadatas: [ updatedMetadata] ,
1102+ keepExistingDownloadState: true
1103+ )
1104+
1105+ XCTAssertEqual ( result. newMetadatas? . isEmpty, true , " Should create no new metadatas " )
1106+ XCTAssertEqual ( result. updatedMetadatas? . isEmpty, false , " Should update existing metadata " )
1107+
1108+ // Verify keepDownloaded is retained
1109+ let finalMetadata = try XCTUnwrap ( result. updatedMetadatas? . first)
1110+ XCTAssertTrue ( finalMetadata. keepDownloaded, " keepDownloaded should be retained during update " )
1111+ XCTAssertTrue ( finalMetadata. downloaded, " downloaded should be retained during update " )
1112+ XCTAssertEqual ( finalMetadata. etag, " new-etag " , " etag should be updated " )
1113+
1114+ // Verify in database
1115+ let dbMetadata = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " test-keep-downloaded " ) )
1116+ XCTAssertTrue ( dbMetadata. keepDownloaded, " keepDownloaded should be retained in database " )
1117+ }
1118+
1119+ func testKeepDownloadedRetainedWithKeepExistingDownloadStateFalse( ) throws {
1120+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1121+
1122+ // Create initial metadata with keepDownloaded = true
1123+ var initialMetadata = SendableItemMetadata ( ocId: " test-keep-downloaded-false " , fileName: " test.txt " , account: account)
1124+ initialMetadata. downloaded = true
1125+ initialMetadata. uploaded = true
1126+ initialMetadata. keepDownloaded = true
1127+ initialMetadata. etag = " old-etag "
1128+
1129+ Self . dbManager. addItemMetadata ( initialMetadata)
1130+
1131+ // Update metadata with new etag
1132+ var updatedMetadata = initialMetadata
1133+ updatedMetadata. etag = " new-etag "
1134+ updatedMetadata. keepDownloaded = false // This would be the case when converting from NKFile
1135+ updatedMetadata. downloaded = false // Set to false to test keepDownloaded retention
1136+
1137+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1138+ account: account. ncKitAccount,
1139+ serverUrl: account. davFilesUrl,
1140+ updatedMetadatas: [ updatedMetadata] ,
1141+ keepExistingDownloadState: false // Even when not keeping download state
1142+ )
1143+
1144+ XCTAssertEqual ( result. updatedMetadatas? . isEmpty, false , " Should update existing metadata " )
1145+
1146+ // Verify keepDownloaded is still retained even when keepExistingDownloadState is false
1147+ let finalMetadata = try XCTUnwrap ( result. updatedMetadatas? . first)
1148+ XCTAssertTrue ( finalMetadata. keepDownloaded, " keepDownloaded should be retained regardless of keepExistingDownloadState " )
1149+ XCTAssertEqual ( finalMetadata. downloaded, false , " downloaded should not be retained when keepExistingDownloadState is false " )
1150+ }
1151+
1152+ func testKeepDownloadedRetainedInReadTargetMetadata( ) throws {
1153+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1154+
1155+ // Create existing metadata with keepDownloaded = true
1156+ var existingMetadata = SendableItemMetadata ( ocId: " read-target-test " , fileName: " target.txt " , account: account)
1157+ existingMetadata. keepDownloaded = true
1158+ existingMetadata. downloaded = true
1159+ existingMetadata. status = Status . normal. rawValue
1160+
1161+ Self . dbManager. addItemMetadata ( existingMetadata)
1162+
1163+ // Create new read target metadata (simulating reading from server)
1164+ var readTargetMetadata = SendableItemMetadata ( ocId: " read-target-test " , fileName: " target.txt " , account: account)
1165+ readTargetMetadata. etag = " new-etag "
1166+ readTargetMetadata. keepDownloaded = false // Would be false when created from NKFile
1167+ readTargetMetadata. downloaded = false
1168+
1169+ // This simulates the path in depth1ReadUpdateItemMetadatas where readTargetMetadata
1170+ // is processed and existing properties should be retained
1171+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1172+ account: account. ncKitAccount,
1173+ serverUrl: account. davFilesUrl,
1174+ updatedMetadatas: [ readTargetMetadata] ,
1175+ keepExistingDownloadState: true
1176+ )
1177+
1178+ let updatedMetadata = try XCTUnwrap ( result. updatedMetadatas? . first)
1179+ XCTAssertTrue ( updatedMetadata. keepDownloaded, " keepDownloaded should be retained in read target metadata " )
1180+ XCTAssertTrue ( updatedMetadata. downloaded, " downloaded should be retained when keepExistingDownloadState is true " )
1181+ }
1182+
1183+ func testKeepDownloadedNotSetForNewMetadata( ) throws {
1184+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1185+
1186+ // Create completely new metadata (not existing in database)
1187+ var newMetadata = SendableItemMetadata ( ocId: " new-item " , fileName: " new.txt " , account: account)
1188+ newMetadata. etag = " initial-etag "
1189+ newMetadata. keepDownloaded = false // Should remain false for new items
1190+
1191+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1192+ account: account. ncKitAccount,
1193+ serverUrl: account. davFilesUrl,
1194+ updatedMetadatas: [ newMetadata] ,
1195+ keepExistingDownloadState: true
1196+ )
1197+
1198+ XCTAssertEqual ( result. newMetadatas? . isEmpty, false , " Should create new metadata " )
1199+ XCTAssertEqual ( result. updatedMetadatas? . isEmpty, true , " Should not update any metadata " )
1200+
1201+ let createdMetadata = try XCTUnwrap ( result. newMetadatas? . first)
1202+ XCTAssertFalse ( createdMetadata. keepDownloaded, " keepDownloaded should remain false for new items " )
1203+
1204+ // Verify in database
1205+ let dbMetadata = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " new-item " ) )
1206+ XCTAssertFalse ( dbMetadata. keepDownloaded, " keepDownloaded should be false in database for new items " )
1207+ }
1208+
1209+ func testKeepDownloadedRetainedWithMultipleItems( ) throws {
1210+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1211+
1212+ var parentFolder = SendableItemMetadata ( ocId: " pf " , fileName: " pf " , account: account)
1213+ parentFolder. uploaded = true
1214+ parentFolder. etag = " old-pf "
1215+
1216+ // Create multiple items with different keepDownloaded states
1217+ var item1 = SendableItemMetadata ( ocId: " multi-1 " , fileName: " file1.txt " , account: account)
1218+ item1. keepDownloaded = true
1219+ item1. downloaded = true
1220+ item1. uploaded = true
1221+ item1. etag = " old-1 "
1222+ item1. serverUrl = account. davFilesUrl. appending ( " /pf " )
1223+
1224+ var item2 = SendableItemMetadata ( ocId: " multi-2 " , fileName: " file2.txt " , account: account)
1225+ item2. keepDownloaded = false
1226+ item2. downloaded = false
1227+ item2. uploaded = true
1228+ item2. etag = " old-2 "
1229+ item2. serverUrl = account. davFilesUrl. appending ( " /pf " )
1230+
1231+ var item3 = SendableItemMetadata ( ocId: " multi-3 " , fileName: " file3.txt " , account: account)
1232+ item3. keepDownloaded = true
1233+ item3. downloaded = false
1234+ item3. uploaded = true
1235+ item3. etag = " old-3 "
1236+ item3. serverUrl = account. davFilesUrl. appending ( " /pf " )
1237+
1238+ Self . dbManager. addItemMetadata ( parentFolder)
1239+ Self . dbManager. addItemMetadata ( item1)
1240+ Self . dbManager. addItemMetadata ( item2)
1241+ Self . dbManager. addItemMetadata ( item3)
1242+
1243+ // Update all items with new etags
1244+ var updatedParentFolder = parentFolder
1245+ updatedParentFolder. etag = " new-pf "
1246+
1247+ var updatedItem1 = item1
1248+ updatedItem1. etag = " new-1 "
1249+ updatedItem1. keepDownloaded = false // Would be reset when converting from NKFile
1250+
1251+ var updatedItem2 = item2
1252+ updatedItem2. etag = " new-2 "
1253+ updatedItem2. keepDownloaded = false
1254+
1255+ var updatedItem3 = item3
1256+ updatedItem3. etag = " new-3 "
1257+ updatedItem3. keepDownloaded = false
1258+
1259+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1260+ account: account. ncKitAccount,
1261+ serverUrl: account. davFilesUrl. appending ( " /pf " ) ,
1262+ updatedMetadatas: [ updatedParentFolder, updatedItem1, updatedItem2, updatedItem3] ,
1263+ keepExistingDownloadState: true
1264+ )
1265+
1266+ XCTAssertEqual ( result. updatedMetadatas? . count, 4 , " Should update all four items " )
1267+
1268+ // Verify each item's keepDownloaded state is correctly retained
1269+ let updatedMetadatas = try XCTUnwrap ( result. updatedMetadatas)
1270+
1271+ let finalItem1 = try XCTUnwrap ( updatedMetadatas. first { $0. ocId == " multi-1 " } )
1272+ XCTAssertTrue ( finalItem1. keepDownloaded, " Item 1 should retain keepDownloaded = true " )
1273+
1274+ let finalItem2 = try XCTUnwrap ( updatedMetadatas. first { $0. ocId == " multi-2 " } )
1275+ XCTAssertFalse ( finalItem2. keepDownloaded, " Item 2 should retain keepDownloaded = false " )
1276+
1277+ let finalItem3 = try XCTUnwrap ( updatedMetadatas. first { $0. ocId == " multi-3 " } )
1278+ XCTAssertTrue ( finalItem3. keepDownloaded, " Item 3 should retain keepDownloaded = true " )
1279+ }
1280+
10761281 func testParentItemIdentifierWithRemoteFallback( ) async throws {
10771282 let rootItem = MockRemoteItem . rootItem ( account: Self . account)
10781283 let remoteFolder = MockRemoteItem (
@@ -1197,6 +1402,49 @@ final class FilesDatabaseManagerTests: XCTestCase {
11971402 XCTAssertTrue ( materialisedOcIds. contains ( sDirD. ocId) )
11981403 }
11991404
1405+ func testKeepDownloadedRetainedDuringUpdate( ) throws {
1406+ let account = Account ( user: " test " , id: " t " , serverUrl: " https://example.com " , password: " " )
1407+
1408+ // Create initial metadata with keepDownloaded = true
1409+ var initialMetadata = SendableItemMetadata ( ocId: " test-keep-downloaded " , fileName: " test.txt " , account: account)
1410+ initialMetadata. downloaded = true
1411+ initialMetadata. uploaded = true
1412+ initialMetadata. keepDownloaded = true
1413+ initialMetadata. etag = " old-etag "
1414+
1415+ Self . dbManager. addItemMetadata ( initialMetadata)
1416+
1417+ // Verify initial state
1418+ let storedMetadata = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " test-keep-downloaded " ) )
1419+ XCTAssertTrue ( storedMetadata. keepDownloaded, " Initial keepDownloaded should be true " )
1420+ XCTAssertTrue ( storedMetadata. downloaded, " Initial downloaded should be true " )
1421+
1422+ // Update metadata with new etag (simulating server update)
1423+ var updatedMetadata = initialMetadata
1424+ updatedMetadata. etag = " new-etag "
1425+ updatedMetadata. keepDownloaded = false // This would be the case when converting from NKFile
1426+
1427+ let result = Self . dbManager. depth1ReadUpdateItemMetadatas (
1428+ account: account. ncKitAccount,
1429+ serverUrl: account. davFilesUrl,
1430+ updatedMetadatas: [ updatedMetadata] ,
1431+ keepExistingDownloadState: true
1432+ )
1433+
1434+ XCTAssertEqual ( result. newMetadatas? . isEmpty, true , " Should create no new metadatas " )
1435+ XCTAssertEqual ( result. updatedMetadatas? . isEmpty, false , " Should update existing metadata " )
1436+
1437+ // Verify keepDownloaded is retained
1438+ let finalMetadata = try XCTUnwrap ( result. updatedMetadatas? . first)
1439+ XCTAssertTrue ( finalMetadata. keepDownloaded, " keepDownloaded should be retained during update " )
1440+ XCTAssertTrue ( finalMetadata. downloaded, " downloaded should be retained during update " )
1441+ XCTAssertEqual ( finalMetadata. etag, " new-etag " , " etag should be updated " )
1442+
1443+ // Verify in database
1444+ let dbMetadata = try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: " test-keep-downloaded " ) )
1445+ XCTAssertTrue ( dbMetadata. keepDownloaded, " keepDownloaded should be retained in database " )
1446+ }
1447+
12001448 func testPendingWorkingSetChanges( ) {
12011449 // 1. Arrange
12021450 let anchorDate = Date ( ) . addingTimeInterval ( - 300 ) // 5 minutes ago
0 commit comments