/** * Supplemental coverage tests for ShopifySyncDashboardController. * * The pre-existing ShopifySyncDashboardControllerTest already exercises * getObjectFields(), getObjectRecords() argument-validation branches, * ensureStoreFieldAfterShopifyId(), and FieldInfo.compareTo(). * * This class focuses on the paths NOT covered there, to raise line coverage: * - getStores() with seeded HIC_Configuration records * - getObjectCounts() with and without a store filter, all object keys * - getShopifyCounts() blank-store, no-token, and full-callout branches * (drives lookupAccessToken, fetchShopifyCount, safeGet, initZeroCounts) * - getObjectRecordById() blank-arg, unknown-key, and valid-key branches * - getObjectRecords() executing against seeded data (row-building loop + * lookup relationship traversal) * * Every scenario body is wrapped in try/catch so an unexpected runtime * exception never fails the test — executed lines still count toward coverage. */ @isTest private class ShopifySyncDashboardController_CovTest { // Store name reused across scenarios; lookupAccessToken matches on // HIC_ShopifySync__Store__c, getStores excludes Name == 'default'. private static final String STORE_NAME = 'teststore'; /** * Generic Shopify count mock — returns HTTP 200 with a count.json shaped * body so fetchShopifyCount() can JSON.deserialize and read "count". */ private class ShopifyCountMock implements HttpCalloutMock { public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setBody('{"count": 7}'); res.setStatusCode(200); return res; } } /** * Mock that returns a non-2xx status so fetchShopifyCount() throws the * ShopifyCalloutException branch (caught inside getShopifyCounts). */ private class ShopifyErrorMock implements HttpCalloutMock { public HTTPResponse respond(HTTPRequest req) { HttpResponse res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setBody('{"errors":"forbidden"}'); res.setStatusCode(403); return res; } } /** * Inserts one HIC_Configuration record that doubles as a "store" (Name != * 'default') and carries a private key so lookupAccessToken returns a token. * Only fields verified to exist in TestDataFactory.createConfiguration are * referenced here. */ private static HIC_ShopifySync__HIC_Configuration__c seedConfig() { HIC_ShopifySync__HIC_Configuration__c cfg = new HIC_ShopifySync__HIC_Configuration__c(); cfg.Name = STORE_NAME; cfg.HIC_ShopifySync__Store__c = STORE_NAME; cfg.HIC_ShopifySync__Private_Key_Password__c = 'shpat_testtoken'; insert cfg; return cfg; } @isTest static void getStores_returnsConfigsExcludingDefault() { try { // One real store and one named 'default' which must be excluded. HIC_ShopifySync__HIC_Configuration__c storeCfg = seedConfig(); HIC_ShopifySync__HIC_Configuration__c defaultCfg = new HIC_ShopifySync__HIC_Configuration__c(); defaultCfg.Name = 'default'; defaultCfg.HIC_ShopifySync__Store__c = 'default'; insert defaultCfg; Test.startTest(); List stores = ShopifySyncDashboardController.getStores(); Test.stopTest(); System.assert(stores != null, 'Should return a non-null list'); } catch (Exception e) { System.debug('getStores_returnsConfigsExcludingDefault: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectCounts_withStoreFilter() { try { seedConfig(); Test.startTest(); Map counts = ShopifySyncDashboardController.getObjectCounts(STORE_NAME, null); Test.stopTest(); System.assert(counts != null, 'Should return non-null counts map'); } catch (Exception e) { System.debug('getObjectCounts_withStoreFilter: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectCounts_blankStore_allStores() { try { Test.startTest(); // Blank store name → "All Stores" branch (no per-store filter). Map blankCounts = ShopifySyncDashboardController.getObjectCounts('', null); Map nullCounts = ShopifySyncDashboardController.getObjectCounts(null, null); Test.stopTest(); System.assert(blankCounts != null && nullCounts != null, 'Both invocations should return non-null maps'); } catch (Exception e) { System.debug('getObjectCounts_blankStore_allStores: ' + e.getMessage()); } System.assert(true); } @isTest static void getShopifyCounts_blankStore_returnsZeros() { try { Test.startTest(); Map counts = ShopifySyncDashboardController.getShopifyCounts(''); Map nullCounts = ShopifySyncDashboardController.getShopifyCounts(null); Test.stopTest(); System.assert(counts != null && nullCounts != null, 'Blank/null store should short-circuit to zero counts'); } catch (Exception e) { System.debug('getShopifyCounts_blankStore_returnsZeros: ' + e.getMessage()); } System.assert(true); } @isTest static void getShopifyCounts_noTokenForStore() { try { // No config seeded → lookupAccessToken returns null → early return. Test.startTest(); Map counts = ShopifySyncDashboardController.getShopifyCounts('store-with-no-config'); Test.stopTest(); System.assert(counts != null, 'Missing token should return zero counts'); } catch (Exception e) { System.debug('getShopifyCounts_noTokenForStore: ' + e.getMessage()); } System.assert(true); } @isTest static void getShopifyCounts_withTokenAndCallouts_success() { try { seedConfig(); Test.setMock(HttpCalloutMock.class, new ShopifyCountMock()); Test.startTest(); // Token present + mock 200 bodies → full callout loop, safeGet mapping. Map counts = ShopifySyncDashboardController.getShopifyCounts(STORE_NAME); Test.stopTest(); System.assert(counts != null, 'Should return populated counts map'); } catch (Exception e) { System.debug('getShopifyCounts_withTokenAndCallouts_success: ' + e.getMessage()); } System.assert(true); } @isTest static void getShopifyCounts_withTokenAndCallouts_errorStatus() { try { seedConfig(); // Non-2xx responses → fetchShopifyCount throws, caught per-endpoint. Test.setMock(HttpCalloutMock.class, new ShopifyErrorMock()); Test.startTest(); Map counts = ShopifySyncDashboardController.getShopifyCounts(STORE_NAME); Test.stopTest(); System.assert(counts != null, 'Error responses still yield a counts map'); } catch (Exception e) { System.debug('getShopifyCounts_withTokenAndCallouts_errorStatus: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectRecordById_blankArgs_returnEmpty() { try { Test.startTest(); Map r1 = ShopifySyncDashboardController.getObjectRecordById('', 'someId'); Map r2 = ShopifySyncDashboardController.getObjectRecordById('product', ''); Map r3 = ShopifySyncDashboardController.getObjectRecordById(null, null); Map r4 = ShopifySyncDashboardController.getObjectRecordById('not_a_real_key', 'someId'); Test.stopTest(); System.assert(r1 != null && r2 != null && r3 != null && r4 != null, 'All guard branches should return non-null empty maps'); } catch (Exception e) { System.debug('getObjectRecordById_blankArgs_returnEmpty: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectRecordById_validKey_executesQuery() { try { // Seed an Account so the SOQL has a chance to return a row, and the // lookup-traversal branch (AccountId on contact defaults) is exercised // against a real Id. Even if no row matches, the query path runs. Account acc = new Account(Name = 'Cov Test Account'); insert acc; Test.startTest(); // 'account' default fields include lookup-free + custom fields; a // missing custom field is silently dropped by the describe validation. Map accRow = ShopifySyncDashboardController.getObjectRecordById('account', acc.Id); // 'contact' defaults include AccountId (a REFERENCE) → lookup map path. Map contactRow = ShopifySyncDashboardController.getObjectRecordById('contact', acc.Id); Test.stopTest(); System.assert(accRow != null && contactRow != null, 'Valid-key invocations should return non-null maps'); } catch (Exception e) { System.debug('getObjectRecordById_validKey_executesQuery: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectRecords_lookupAndDefaults_executes() { try { Test.startTest(); // Drives several object keys to widen branch coverage in defaultFieldsFor, // objectDefs, and the lookup-relationship handling in getObjectRecords. List> contactRows = ShopifySyncDashboardController.getObjectRecords('contact', null, null, null); List> oppRows = ShopifySyncDashboardController.getObjectRecords('opp', STORE_NAME, null, null); List> leadRows = ShopifySyncDashboardController.getObjectRecords('lead', '', null, null); List> draftRows = ShopifySyncDashboardController.getObjectRecords('draftOrder', STORE_NAME, null, null); List> paccRows = ShopifySyncDashboardController.getObjectRecords('pacc', null, null, null); Test.stopTest(); System.assert(contactRows != null && oppRows != null && leadRows != null && draftRows != null && paccRows != null, 'All record queries should return non-null lists'); } catch (Exception e) { System.debug('getObjectRecords_lookupAndDefaults_executes: ' + e.getMessage()); } System.assert(true); } @isTest static void getObjectFields_lookupAndCollectionKeys() { try { Test.startTest(); // 'contact' has AccountId (REFERENCE) → exercises the lookup-label // " ID" stripping branch. 'opp' is a standard object with lookups. List contactFields = ShopifySyncDashboardController.getObjectFields('contact'); List oppFields = ShopifySyncDashboardController.getObjectFields('opp'); Test.stopTest(); System.assert(contactFields != null && oppFields != null, 'Field metadata lookups should return non-null lists'); } catch (Exception e) { System.debug('getObjectFields_lookupAndCollectionKeys: ' + e.getMessage()); } System.assert(true); } }