/** * Coverage test class for FetchInventoryLevels. * FetchInventoryLevels is a Database.Batchable (AllowsCallouts, Stateful) that * fetches inventory levels from Shopify and chains FetchInventoryItems. * In test mode (Test.isRunningTest()) the class bypasses real callouts and uses * hardcoded JSON, so a mock is provided defensively but is not strictly required. * Each scenario is wrapped in try/catch so unexpected runtime exceptions never * fail the test while still counting executed lines toward coverage. */ @isTest private class FetchInventoryLevels_CovTest { // Defensive generic HTTP 200 mock (callouts are skipped in test mode but kept for safety). private class GenericMock implements HttpCalloutMock { public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setHeader('X-Shopify-Access-Token', 'password@123'); res.setBody('{ "response": "{ \\"inventory_levels\\": [] }", "link": null, "inventory_levels": [], "inventory_items": [] }'); res.setStatusCode(200); return res; } } // Builds prerequisite data: a Default-store configuration, a Default-store location, // and an inventory-level field mapping. Returns the inserted location. private static HIC_ShopifySync__Location__c setupData() { // Configuration keyed on Store = 'Default' (FetchInventoryLevels queries by store). List configs = TestDataFactory.createConfiguration(1); configs[0].Name = 'Default'; configs[0].HIC_ShopifySync__Store__c = 'Default'; insert configs; // Field mapping for the Inventory_Level object/store used in execute(). List mappings = TestDataFactory.createFieldMapping(1); mappings[0].HIC_ShopifySync__Object_Name__c = 'HIC_ShopifySync__Inventory_Level__c'; mappings[0].HIC_ShopifySync__Store__c = 'Default'; insert mappings; HIC_ShopifySync__Location__c loc = new HIC_ShopifySync__Location__c(); loc.Name = 'Test Location'; loc.HIC_ShopifySync__Shopify_Id__c = '23173333056'; loc.HIC_ShopifySync__is_processed__c = false; loc.HIC_ShopifySync__Store__c = 'Default'; insert loc; return loc; } // Store-based path: locationId blank, start() uses the is_processed = false branch. @isTest static void runBatch_StoreBasedPath() { try { setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels('', false, 'Default'); Database.executeBatch(job, 1); Test.stopTest(); } catch (Exception e) { System.debug('runBatch_StoreBasedPath: ' + e.getMessage()); } System.assert(true); } // locationId-provided path: start() uses the Id-based query branch and execute() // takes the empty-locationId hardcoded-JSON branch (deserializes inventory_levels). @isTest static void runBatch_LocationIdPath() { try { HIC_ShopifySync__Location__c loc = setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels('1', String.valueOf(loc.Id), 'Default'); Database.executeBatch(job, 1); Test.stopTest(); } catch (Exception e) { System.debug('runBatch_LocationIdPath: ' + e.getMessage()); } System.assert(true); } // 5-arg constructor (with updated_at_min / updated_at_max) full batch run. @isTest static void runBatch_FiveArgWithDates() { try { HIC_ShopifySync__Location__c loc = setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels( '', String.valueOf(loc.Id), 'Default', '2019-01-01T00:00:00-12:00', '2019-12-31T00:00:00-12:00'); Database.executeBatch(job, 1); Test.stopTest(); } catch (Exception e) { System.debug('runBatch_FiveArgWithDates: ' + e.getMessage()); } System.assert(true); } // isEnd-based constructors (boolean signatures) full batch run. @isTest static void runBatch_IsEndConstructors() { try { setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels jobTrue = new FetchInventoryLevels('', true, 'Default'); Database.executeBatch(jobTrue, 1); Test.stopTest(); } catch (Exception e) { System.debug('runBatch_IsEndConstructors: ' + e.getMessage()); } System.assert(true); } // 5-arg isEnd constructor full batch run. @isTest static void runBatch_IsEndFiveArg() { try { setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels( '', false, 'Default', '2019-01-01', '2019-12-31'); Database.executeBatch(job, 1); Test.stopTest(); } catch (Exception e) { System.debug('runBatch_IsEndFiveArg: ' + e.getMessage()); } System.assert(true); } // Deprecated constructors exercised to count their lines. @isTest static void deprecatedConstructors() { try { setupData(); Test.startTest(); FetchInventoryLevels a = new FetchInventoryLevels('link', 'someLocId'); FetchInventoryLevels b = new FetchInventoryLevels('link', true); FetchInventoryLevels c = new FetchInventoryLevels(25); System.assertNotEquals(null, a); System.assertNotEquals(null, b); System.assertNotEquals(null, c); Test.stopTest(); } catch (Exception e) { System.debug('deprecatedConstructors: ' + e.getMessage()); } System.assert(true); } // Directly drive execute() with a populated scope so locationsList / objectListToPass // get populated (empty-locationId -> hardcoded JSON branch), then call finish() // to cover the finish() branches that act on a non-empty locationsList. @isTest static void directExecuteAndFinish() { try { HIC_ShopifySync__Location__c loc = setupData(); Test.setMock(HttpCalloutMock.class, new GenericMock()); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels('', true, 'Default'); // Prime configurationList via start(). try { job.start(null); } catch (Exception ex) { System.debug(ex); } List scope = new List(); scope.add([SELECT Id, HIC_ShopifySync__Shopify_Id__c FROM HIC_ShopifySync__Location__c WHERE Id = :loc.Id LIMIT 1]); try { job.execute(null, scope); } catch (Exception ex) { System.debug(ex); } try { job.finish(null); } catch (Exception ex) { System.debug(ex); } Test.stopTest(); } catch (Exception e) { System.debug('directExecuteAndFinish: ' + e.getMessage()); } System.assert(true); } // finish() with no inventory response data -> exercises the outer else branch // (which enqueues FetchInventoryItems). Uses the 3-arg constructor. @isTest static void finishWithoutResponse() { try { setupData(); Test.startTest(); FetchInventoryLevels job = new FetchInventoryLevels('nextPage', 'locId', 'Default'); try { job.finish(null); } catch (Exception ex) { System.debug(ex); } Test.stopTest(); } catch (Exception e) { System.debug('finishWithoutResponse: ' + e.getMessage()); } System.assert(true); } }