@@ -398,7 +398,6 @@ final class EnumeratorTests: XCTestCase {
398398 // as pages. This process continues until all directories have been enumerated.
399399 // We can verify that all items are discovered and stored in the database.
400400 let allItemIds = [
401- rootItem. identifier,
402401 folder1. identifier,
403402 folder2. identifier,
404403 subfolder. identifier
@@ -407,8 +406,211 @@ final class EnumeratorTests: XCTestCase {
407406 + subfolder. children. map ( \. identifier)
408407
409408 for itemId in allItemIds {
410- XCTAssertNotNil ( Self . dbManager. itemMetadata ( ocId: itemId) , " Item with id \( itemId) should be in the database " )
409+ XCTAssertNotNil (
410+ Self . dbManager. itemMetadata ( ocId: itemId) ,
411+ " Item with id \( itemId) should be in the database "
412+ )
413+ }
414+ // Root should not be stored in database
415+ XCTAssertNil ( Self . dbManager. itemMetadata ( ocId: rootItem. identifier) )
416+ }
417+
418+ func testReadServerUrlFollowUpPagination( ) async throws {
419+ // 1. Arrange: Setup a folder with enough children to require multiple pages.
420+ remoteFolder. children = [ ]
421+ for i in 0 ..< 10 {
422+ let childItem = MockRemoteItem (
423+ identifier: " folderChild \( i) " ,
424+ name: " folderChild \( i) .txt " ,
425+ remotePath: Self . account. davFilesUrl + " /folder/folderChild \( i) .txt " ,
426+ account: Self . account. ncKitAccount,
427+ username: Self . account. username,
428+ userId: Self . account. id,
429+ serverUrl: Self . account. serverUrl
430+ )
431+ childItem. parent = remoteFolder
432+ remoteFolder. children. append ( childItem)
433+ }
434+
435+ let db = Self . dbManager. ncDatabase ( )
436+ debugPrint ( db)
437+ let remoteInterface = MockRemoteInterface ( rootItem: rootItem, pagination: true )
438+
439+ // Pre-populate the folder's metadata with an old etag to verify it gets updated
440+ // on the initial call.
441+ let oldEtag = " OLD_ETAG "
442+ var folderMetadata = remoteFolder. toItemMetadata ( account: Self . account)
443+ folderMetadata. etag = oldEtag
444+ Self . dbManager. addItemMetadata ( folderMetadata)
445+
446+ // --- Scenario A: Initial Paginated Request (isFollowUpPaginatedRequest == false) ---
447+
448+ // 2. Act: Call readServerUrl for the first page.
449+ let (
450+ initialMetadatas, _, _, _, initialNextPage, initialError
451+ ) = await Enumerator . readServerUrl (
452+ remoteFolder. remotePath,
453+ pageSettings: ( page: nil , index: 0 , size: 5 ) , // index is 0
454+ account: Self . account,
455+ remoteInterface: remoteInterface,
456+ dbManager: Self . dbManager
457+ )
458+
459+ // 3. Assert: Verify the initial request's behavior.
460+ XCTAssertNil ( initialError)
461+ XCTAssertNotNil ( initialNextPage, " Should receive a next page token for the initial request " )
462+ // The first request for a folder returns the folder itself plus the first page of children.
463+ XCTAssertEqual (
464+ initialMetadatas? . count,
465+ 4 ,
466+ """
467+ Should get target + first page of children,
468+ but the target should not be included in the first page,
469+ so count is (4).
470+ """
471+ )
472+ XCTAssertFalse (
473+ initialMetadatas? . contains ( where: { $0. ocId == remoteFolder. identifier } ) ?? false ,
474+ " The folder itself should not be in the initial results. "
475+ )
476+
477+ // The logic inside `if !isFollowUpPaginatedRequest` should have run,
478+ // updating the folder's metadata.
479+ let updatedFolderMetadata =
480+ try XCTUnwrap ( Self . dbManager. itemMetadata ( ocId: remoteFolder. identifier) )
481+ XCTAssertNotEqual (
482+ updatedFolderMetadata. etag, oldEtag, " The folder's etag should have been updated. "
483+ )
484+ XCTAssertEqual ( updatedFolderMetadata. etag, remoteFolder. versionIdentifier)
485+
486+ // --- Scenario B: Follow-up Paginated Request (isFollowUpPaginatedRequest == true) ---
487+
488+ // 4. Act: Call readServerUrl for the second page using the received page token.
489+ let followUpPage = NSFileProviderPage ( initialNextPage!. token. data ( using: . utf8) !)
490+ let (
491+ followUpMetadatas, _, _, _, finalNextPage, followUpError
492+ ) = await Enumerator . readServerUrl (
493+ remoteFolder. remotePath,
494+ pageSettings: ( page: followUpPage, index: 1 , size: 5 ) , // index > 0 and page is non-nil
495+ account: Self . account,
496+ remoteInterface: remoteInterface,
497+ dbManager: Self . dbManager
498+ )
499+
500+ // 5. Assert: Verify the follow-up request's behavior.
501+ XCTAssertNil ( followUpError)
502+ XCTAssertNotNil ( finalNextPage, " Should receive a next page token for the second request. " )
503+ // A follow-up request should *only* contain the children for that page.
504+ XCTAssertEqual (
505+ followUpMetadatas? . count, 5 , " Should get only the second page of children (5). "
506+ )
507+ XCTAssertFalse (
508+ followUpMetadatas? . contains ( where: { $0. ocId == remoteFolder. identifier } ) ?? true ,
509+ " The folder itself should NOT be in the follow-up page results. "
510+ )
511+
512+ // This confirms the `if !isFollowUpPaginatedRequest` block was correctly skipped, as it'd
513+ // have processed the first item of the result array (`folderChild5`) as the parent folder,
514+ // which would lead to incorrect data or errors.
515+ let child5Metadata = Self . dbManager. itemMetadata ( ocId: " folderChild5 " )
516+ XCTAssertNotNil (
517+ child5Metadata, " Metadata for the items on the second page should be in the database. "
518+ )
519+ }
520+
521+ func testHandlePagedReadResults( ) throws {
522+ // 1. Arrange
523+ let dbManager = Self . dbManager
524+ let db = dbManager. ncDatabase ( )
525+ debugPrint ( db)
526+
527+ let parentNKFile = remoteFolder. toNKFile ( )
528+ let childrenNKFiles = ( 0 ..< 5 ) . map { i in
529+ MockRemoteItem (
530+ identifier: " pagedChild \( i) " ,
531+ name: " pagedChild \( i) .txt " ,
532+ remotePath: Self . account. davFilesUrl + " /folder/pagedChild \( i) .txt " ,
533+ account: Self . account. ncKitAccount,
534+ username: Self . account. username,
535+ userId: Self . account. id,
536+ serverUrl: Self . account. serverUrl
537+ ) . toNKFile ( )
538+ }
539+ let followUpChildrenNKFiles = ( 5 ..< 10 ) . map { i in
540+ MockRemoteItem (
541+ identifier: " pagedChild \( i) " ,
542+ name: " pagedChild \( i) .txt " ,
543+ remotePath: Self . account. davFilesUrl + " /folder/pagedChild \( i) .txt " ,
544+ account: Self . account. ncKitAccount,
545+ username: Self . account. username,
546+ userId: Self . account. id,
547+ serverUrl: Self . account. serverUrl
548+ ) . toNKFile ( )
411549 }
550+
551+ // --- Scenario A: First Page (pageIndex == 0) ---
552+ // 2. Act
553+ let firstPageFiles = [ parentNKFile] + childrenNKFiles
554+ let ( firstPageResult, firstPageError) = Enumerator . handlePagedReadResults (
555+ files: firstPageFiles, pageIndex: 0 , dbManager: dbManager
556+ )
557+
558+ // 3. Assert
559+ XCTAssertNil ( firstPageError)
560+ // Result should only contain the children, not the parent.
561+ XCTAssertEqual (
562+ firstPageResult? . count, 5 , " First page result should only contain children. "
563+ )
564+ // The parent's metadata should be processed and saved to the DB.
565+ let parentMetadataFromDB = dbManager. itemMetadata ( ocId: parentNKFile. ocId)
566+ XCTAssertNotNil (
567+ parentMetadataFromDB, " Parent folder metadata should be saved to DB from first page. "
568+ )
569+ XCTAssertEqual ( parentMetadataFromDB? . etag, parentNKFile. etag, " Parent etag should match. " )
570+ // The children's metadata should also be saved.
571+ let childMetadataFromDB = dbManager. itemMetadata ( ocId: " pagedChild0 " )
572+ XCTAssertNotNil (
573+ childMetadataFromDB, " Child metadata should be saved to DB from first page. "
574+ )
575+
576+ // --- Scenario B: Follow-up Page (pageIndex > 0) ---
577+ // 4. Act
578+ let ( followUpPageResult, followUpPageError) = Enumerator . handlePagedReadResults (
579+ files: followUpChildrenNKFiles, pageIndex: 1 , dbManager: dbManager
580+ )
581+
582+ // 5. Assert
583+ XCTAssertNil ( followUpPageError)
584+ // Result should contain all items passed in, as the parent is not included.
585+ XCTAssertEqual (
586+ followUpPageResult? . count, 5 , " Follow-up page result should contain all its items. "
587+ )
588+ let followUpChildMetadata = dbManager. itemMetadata ( ocId: " pagedChild5 " )
589+ XCTAssertNotNil (
590+ followUpChildMetadata, " Child metadata should be saved to DB from follow-up page. "
591+ )
592+
593+ // --- Scenario C: Root Folder (should not be ingested) ---
594+ // 6. Act
595+ var rootNKFile = MockRemoteItem . rootItem ( account: Self . account) . toNKFile ( )
596+ // The check is based on URL string matching.
597+ rootNKFile. path = Self . account. davFilesUrl
598+
599+ let ( rootResult, rootError) = Enumerator . handlePagedReadResults (
600+ files: [ rootNKFile] , pageIndex: 0 , dbManager: dbManager
601+ )
602+
603+ // 7. Assert
604+ XCTAssertNil ( rootError)
605+ // The result should be empty as there are no children.
606+ XCTAssertEqual (
607+ rootResult? . count, 0 , " Root folder enumeration should yield no children. "
608+ )
609+ // No metadata should be saved for the root folder ID itself.
610+ XCTAssertNil (
611+ dbManager. itemMetadata ( ocId: rootNKFile. ocId) ,
612+ " Metadata for the root folder itself should not be saved. "
613+ )
412614 }
413615
414616 func testWorkingSetChangeEnumeration( ) async throws {
0 commit comments