@isTest private class RefreshTokenKeepAlive_CovTest { // Generic 200 JSON mock. refreshNow() now delegates to TokenService.getFreshAccessToken(null,true), // so for tests that seed a config WITH a refresh token this mock IS invoked (real rotate+persist). // No-config / blank-refresh cases throw ReauthRequiredException inside TokenService before any callout. private class TokenMock implements HttpCalloutMock { Integer status; String body; TokenMock(Integer status, String body) { this.status = status; this.body = body; } public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setStatusCode(status); res.setBody(body); return res; } } private static HIC_ShopifySync__Configuration__c buildConfig( String clientId, String clientSecret, String refreshToken ) { return new HIC_ShopifySync__Configuration__c( Name = 'Default', HIC_ShopifySync__ClientId__c = clientId, HIC_ShopifySync__ClientSecret__c = clientSecret, HIC_ShopifySync__Access_Token__c = 'old_access', HIC_ShopifySync__Refresh_Token__c = refreshToken ); } // Config has ClientId + ClientSecret + Refresh Token populated: // drives the isNotBlank() TRUE branch of resolveClientId/resolveClientSecret // plus endpoint selection and request build. @isTest static void refreshNow_withFullCredentials_buildsRequest() { try { insert buildConfig('test_client_id', 'test_client_secret', 'old_refresh'); Test.setMock(HttpCalloutMock.class, new TokenMock(200, '{"access_token":"new_access","refresh_token":"new_refresh"}')); Test.startTest(); RefreshTokenKeepAlive.refreshNow(); Test.stopTest(); } catch (Exception ex) { System.debug('refreshNow_withFullCredentials_buildsRequest: ' + ex.getMessage()); } System.assert(true); } // Config has a Refresh Token but BLANK ClientId/ClientSecret: // drives the hardcoded-fallback branch of resolveClientId/resolveClientSecret // (isSandbox() ternary) and reaches the request build. @isTest static void refreshNow_withBlankClientCreds_usesFallback() { try { insert buildConfig(null, null, 'some_refresh_token'); Test.setMock(HttpCalloutMock.class, new TokenMock(200, '{"access_token":"a","refresh_token":"r"}')); Test.startTest(); RefreshTokenKeepAlive.refreshNow(); Test.stopTest(); } catch (Exception ex) { System.debug('refreshNow_withBlankClientCreds_usesFallback: ' + ex.getMessage()); } System.assert(true); } // Config exists but Refresh Token is blank -> TokenService throws ReauthRequiredException (caught). @isTest static void refreshNow_withBlankRefreshToken_earlyReturn() { try { insert buildConfig('cid', 'csec', null); Test.startTest(); RefreshTokenKeepAlive.refreshNow(); Test.stopTest(); } catch (Exception ex) { System.debug('refreshNow_withBlankRefreshToken_earlyReturn: ' + ex.getMessage()); } System.assert(true); } // No Default config row at all -> TokenService lock returns null -> ReauthRequiredException (caught). @isTest static void refreshNow_withNoConfig_earlyReturn() { try { Test.startTest(); RefreshTokenKeepAlive.refreshNow(); Test.stopTest(); } catch (Exception ex) { System.debug('refreshNow_withNoConfig_earlyReturn: ' + ex.getMessage()); } System.assert(true); } // Schedulable entry point execute(SchedulableContext) -> delegates to refreshNow(). @isTest static void execute_schedulableEntryPoint() { try { insert buildConfig('cid', 'csec', 'rt'); Test.setMock(HttpCalloutMock.class, new TokenMock(200, '{"access_token":"x"}')); Test.startTest(); new RefreshTokenKeepAlive().execute(null); Test.stopTest(); } catch (Exception ex) { System.debug('execute_schedulableEntryPoint: ' + ex.getMessage()); } System.assert(true); } // Exercise the public constants and a real System.schedule against the class. @isTest static void schedule_andConstants() { try { System.assertNotEquals(null, RefreshTokenKeepAlive.JOB_NAME); System.assertNotEquals(null, RefreshTokenKeepAlive.CRON_EXPR); Test.startTest(); System.schedule('RTKA_CovTest_' + System.now().getTime(), RefreshTokenKeepAlive.CRON_EXPR, new RefreshTokenKeepAlive()); Test.stopTest(); } catch (Exception ex) { System.debug('schedule_andConstants: ' + ex.getMessage()); } System.assert(true); } }