@@ -2687,7 +2687,7 @@ void TestMerginApi::deleteRemoteProjectNow( MerginApi *api, const QString &proje
26872687 QUrl url ( api->mApiRoot + QStringLiteral ( " /v2/projects/%1" ).arg ( projectId ) );
26882688 request.setUrl ( url );
26892689 qDebug () << " Trying to delete project " << projectName << " , id: " << projectId << " (" << url << " )" ;
2690- QNetworkReply *r = api->mManager . deleteResource ( request );
2690+ QNetworkReply *r = api->mManager -> deleteResource ( request );
26912691 QSignalSpy spy ( r, &QNetworkReply::finished );
26922692 spy.wait ( TestUtils::SHORT_REPLY );
26932693
@@ -2942,3 +2942,143 @@ void TestMerginApi::testParseVersion()
29422942 QCOMPARE ( major, 2024 );
29432943 QCOMPARE ( minor, 4 );
29442944}
2945+
2946+ void TestMerginApi::testDownloadWithNetworkError ()
2947+ {
2948+ // Store original manager
2949+ QNetworkAccessManager *originalManager = mApi ->networkManager ();
2950+
2951+ QString projectName = " testDownloadRetry" ;
2952+ createRemoteProject ( mApiExtra , mWorkspaceName , projectName, mTestDataPath + " /" + TEST_PROJECT_NAME + " /" );
2953+
2954+ // Errors to test
2955+ QList<QNetworkReply::NetworkError> errorsToTest =
2956+ {
2957+ QNetworkReply::TimeoutError,
2958+ QNetworkReply::NetworkSessionFailedError
2959+ };
2960+
2961+ foreach ( QNetworkReply::NetworkError networkError, errorsToTest )
2962+ {
2963+ // Create mock manager - initially not failing
2964+ MockNetworkManager *failingManager = new MockNetworkManager ( this );
2965+ mApi ->setNetworkManager ( failingManager );
2966+
2967+ // Create signal spies
2968+ QSignalSpy startSpy ( mApi , &MerginApi::pullFilesStarted );
2969+ QSignalSpy retrySpy ( mApi , &MerginApi::downloadItemRetried );
2970+ QSignalSpy finishSpy ( mApi , &MerginApi::syncProjectFinished );
2971+
2972+ // Trigger the current network error when download starts
2973+ connect ( mApi , &MerginApi::pullFilesStarted, this , [this , failingManager, networkError]()
2974+ {
2975+ failingManager->setShouldFail ( true , networkError );
2976+ } );
2977+
2978+ mApi ->pullProject ( mWorkspaceName , projectName );
2979+
2980+ // Verify a transaction was created
2981+ QCOMPARE ( mApi ->transactions ().count (), 1 );
2982+
2983+ // Wait for download to start and then fail
2984+ QVERIFY ( startSpy.wait ( TestUtils::LONG_REPLY ) );
2985+ QVERIFY ( finishSpy.wait ( TestUtils::LONG_REPLY ) );
2986+
2987+ // Verify signals were emitted
2988+ QVERIFY ( startSpy.count () > 0 );
2989+ QVERIFY ( retrySpy.count () > 0 );
2990+ QCOMPARE ( finishSpy.count (), 1 );
2991+
2992+ // Verify that MAX_RETRY_COUNT retry attempts were made
2993+ int maxRetries = TransactionStatus::MAX_RETRY_COUNT;
2994+ QCOMPARE ( retrySpy.count (), maxRetries );
2995+
2996+ // Verify sync failed
2997+ QList<QVariant> arguments = finishSpy.takeFirst ();
2998+ QVERIFY ( !arguments.at ( 1 ).toBool () );
2999+
3000+ // Verify no local project was created
3001+ LocalProject localProject = mApi ->localProjectsManager ().projectFromMerginName ( mWorkspaceName , projectName );
3002+ QVERIFY ( !localProject.isValid () );
3003+
3004+ // Disconnect all signals
3005+ disconnect ( mApi , &MerginApi::pullFilesStarted, this , nullptr );
3006+
3007+ // Clean up
3008+ mApi ->setNetworkManager ( originalManager );
3009+ delete failingManager;
3010+ }
3011+ }
3012+
3013+ void TestMerginApi::testDownloadWithNetworkErrorRecovery ()
3014+ {
3015+ // Store original manager
3016+ QNetworkAccessManager *originalManager = mApi ->networkManager ();
3017+
3018+ QString projectName = " testDownloadRetryRecovery" ;
3019+ createRemoteProject ( mApiExtra , mWorkspaceName , projectName, mTestDataPath + " /" + TEST_PROJECT_NAME + " /" );
3020+
3021+ // Create mock manager - initially not failing
3022+ MockNetworkManager *failingManager = new MockNetworkManager ( this );
3023+ mApi ->setNetworkManager ( failingManager );
3024+
3025+ // Create signal spies
3026+ QSignalSpy startSpy ( mApi , &MerginApi::pullFilesStarted );
3027+ QSignalSpy retrySpy ( mApi , &MerginApi::downloadItemRetried );
3028+ QSignalSpy finishSpy ( mApi , &MerginApi::syncProjectFinished );
3029+
3030+ // Counter to track retry attempts
3031+ int retryCount = 0 ;
3032+ QNetworkReply::NetworkError networkError = QNetworkReply::TimeoutError;
3033+
3034+ // Reset network after two retries
3035+ connect ( mApi , &MerginApi::downloadItemRetried, this , [&retryCount, failingManager, this ]()
3036+ {
3037+ retryCount++;
3038+ if ( retryCount == 2 )
3039+ {
3040+ failingManager->setShouldFail ( false );
3041+ disconnect ( mApi , &MerginApi::pullFilesStarted, nullptr , nullptr );
3042+ disconnect ( mApi , &MerginApi::downloadItemRetried, nullptr , nullptr );
3043+ }
3044+ } );
3045+
3046+ // Trigger network error when download starts
3047+ connect ( mApi , &MerginApi::pullFilesStarted, this , [failingManager, networkError]()
3048+ {
3049+ failingManager->setShouldFail ( true , networkError );
3050+ } );
3051+
3052+ mApi ->pullProject ( mWorkspaceName , projectName );
3053+
3054+ // Verify a transaction was created
3055+ QCOMPARE ( mApi ->transactions ().count (), 1 );
3056+
3057+ // Wait for download to start, retry twice, and then complete successfully
3058+ QVERIFY ( startSpy.wait ( TestUtils::LONG_REPLY ) );
3059+ QVERIFY ( finishSpy.wait ( TestUtils::LONG_REPLY ) );
3060+
3061+ // Verify signals were emitted
3062+ QVERIFY ( startSpy.count () > 0 );
3063+ QCOMPARE ( retrySpy.count (), 2 ); // Should have exactly 2 retries
3064+ QCOMPARE ( finishSpy.count (), 1 );
3065+
3066+ // Verify sync succeeded
3067+ QList<QVariant> arguments = finishSpy.takeFirst ();
3068+ QVERIFY ( arguments.at ( 1 ).toBool () );
3069+
3070+ // Verify local project was created successfully
3071+ LocalProject localProject = mApi ->localProjectsManager ().projectFromMerginName ( mWorkspaceName , projectName );
3072+ QVERIFY ( localProject.isValid () );
3073+
3074+ // Verify project files were downloaded correctly
3075+ QString projectDir = mApi ->projectsPath () + " /" + projectName;
3076+ QStringList projectFiles = QDir ( projectDir ).entryList ( QDir::Files );
3077+ QVERIFY ( projectFiles.count () > 0 );
3078+ QVERIFY ( projectFiles.contains ( " project.qgs" ) );
3079+
3080+ // Clean up
3081+ mApi ->setNetworkManager ( originalManager );
3082+ delete failingManager;
3083+ }
3084+
0 commit comments