From 5520135943d85811b2f97cb1303f04800121ac1c Mon Sep 17 00:00:00 2001 From: Jonathan Gillespie Date: Tue, 10 Dec 2024 16:03:59 -0500 Subject: [PATCH] Made additional improvements to the tests for LoggerEngineDataSelector and LogManagementDataSelector to improve testing + test coverage --- .../classes/LogManagementDataSelector.cls | 15 ++- .../classes/LoggerEngineDataSelector.cls | 22 ++--- .../LogManagementDataSelector_Tests.cls | 99 ++++++++++++------- .../LoggerEngineDataSelector_Tests.cls | 88 ++++++++++++++++- sfdx-project.json | 4 +- 5 files changed, 173 insertions(+), 55 deletions(-) diff --git a/nebula-logger/core/main/log-management/classes/LogManagementDataSelector.cls b/nebula-logger/core/main/log-management/classes/LogManagementDataSelector.cls index 05062e7d9..8a1c39224 100644 --- a/nebula-logger/core/main/log-management/classes/LogManagementDataSelector.cls +++ b/nebula-logger/core/main/log-management/classes/LogManagementDataSelector.cls @@ -9,6 +9,8 @@ */ @SuppressWarnings('PMD.ApexCRUDViolation, PMD.CyclomaticComplexity, PMD.ExcessivePublicCount') public without sharing virtual class LogManagementDataSelector { + private static final Boolean IS_OMNISTUDIO_ENABLED = System.Type.forName('Schema.OmniProcess') != null; + private static LogManagementDataSelector instance = new LogManagementDataSelector(); @SuppressWarnings('PMD.EmptyStatementBlock') @@ -311,11 +313,14 @@ public without sharing virtual class LogManagementDataSelector { String query = 'SELECT CreatedBy.Username, CreatedById, CreatedDate, Id, IsIntegrationProcedure, LastModifiedBy.Username, LastModifiedById, LastModifiedDate, OmniProcessType, UniqueName' + ' FROM OmniProcess WHERE Id IN :omniProcessIds'; - for (SObject omniProcessRecord : System.Database.query(String.escapeSingleQuotes(query))) { - LoggerSObjectProxy.OmniProcess omniProcessProxy = (LoggerSObjectProxy.OmniProcess) System.JSON.deserialize( - System.JSON.serialize(omniProcessRecord), - LoggerSObjectProxy.OmniProcess.class - ); + List omniProcessRecords = IS_OMNISTUDIO_ENABLED ? System.Database.query(String.escapeSingleQuotes(query)) : new List(); + + List omniProcessProxies = (List) System.JSON.deserialize( + System.JSON.serialize(omniProcessRecords), + List.class + ); + + for (LoggerSObjectProxy.OmniProcess omniProcessProxy : omniProcessProxies) { omniProcessIdToOmniProcessProxy.put(omniProcessProxy.Id, omniProcessProxy); } return omniProcessIdToOmniProcessProxy; diff --git a/nebula-logger/core/main/logger-engine/classes/LoggerEngineDataSelector.cls b/nebula-logger/core/main/logger-engine/classes/LoggerEngineDataSelector.cls index 1d90a0b75..2e14df939 100644 --- a/nebula-logger/core/main/logger-engine/classes/LoggerEngineDataSelector.cls +++ b/nebula-logger/core/main/logger-engine/classes/LoggerEngineDataSelector.cls @@ -92,11 +92,8 @@ public without sharing virtual class LoggerEngineDataSelector { return (LoggerSObjectProxy.AuthSession) LoggerCache.getSessionCache().get(cacheKey); } - LoggerSObjectProxy.AuthSession authSessionProxy; - if (LoggerParameter.QUERY_AUTH_SESSION_DATA) { - authSessionProxy = getAuthSessionProxies(new List{ userId }).get(userId); - LoggerCache.getSessionCache().put(cacheKey, authSessionProxy); - } + LoggerSObjectProxy.AuthSession authSessionProxy = getAuthSessionProxies(new List{ userId }).get(userId); + LoggerCache.getSessionCache().put(cacheKey, authSessionProxy); return authSessionProxy; } @@ -107,7 +104,7 @@ public without sharing virtual class LoggerEngineDataSelector { * @return The cached `Schema.Network` record */ public virtual LoggerSObjectProxy.Network getCachedNetworkProxy(Id networkId) { - if (networkId == null || (IS_EXPERIENCE_CLOUD_ENABLED == false && mockNetworkProxies == null)) { + if (networkId == null) { return null; } @@ -116,11 +113,8 @@ public without sharing virtual class LoggerEngineDataSelector { return (LoggerSObjectProxy.Network) LoggerCache.getOrganizationCache().get(cacheKey); } - LoggerSObjectProxy.Network networkProxy; - if (LoggerParameter.QUERY_NETWORK_DATA) { - networkProxy = getNetworkProxies(new List{ networkId }).get(networkId); - LoggerCache.getOrganizationCache().put(cacheKey, networkProxy); - } + LoggerSObjectProxy.Network networkProxy = getNetworkProxies(new List{ networkId }).get(networkId); + LoggerCache.getOrganizationCache().put(cacheKey, networkProxy); return networkProxy; } @@ -181,14 +175,14 @@ public without sharing virtual class LoggerEngineDataSelector { * @return The instance of `Map` containing any matching `Schema.Network` records */ public Map getNetworkProxies(List networkIds) { - // TODO add caching in a future release + Map networkIdToNetworkProxy = new Map(); if (LoggerParameter.QUERY_NETWORK_DATA == false) { - return null; + return networkIdToNetworkProxy; } // Networks (aka experience sites aka community sites aka portal sites รฒ_รด) // may not be enabled in the org (no Schema.Network object), so run everything dynamically - Map networkIdToNetworkProxy = new Map(); + String query = 'SELECT Id, Name, UrlPathPrefix FROM Network WHERE Id IN :networkIds'; List networkRecords = IS_EXPERIENCE_CLOUD_ENABLED ? System.Database.query(String.escapeSingleQuotes(query)) : new List(); diff --git a/nebula-logger/core/tests/log-management/classes/LogManagementDataSelector_Tests.cls b/nebula-logger/core/tests/log-management/classes/LogManagementDataSelector_Tests.cls index 62eb07791..3d9804efe 100644 --- a/nebula-logger/core/tests/log-management/classes/LogManagementDataSelector_Tests.cls +++ b/nebula-logger/core/tests/log-management/classes/LogManagementDataSelector_Tests.cls @@ -6,8 +6,6 @@ @SuppressWarnings('PMD.ApexDoc, PMD.CyclomaticComplexity, PMD.MethodNamingConventions') @IsTest(IsParallel=false) private class LogManagementDataSelector_Tests { - private static final Boolean IS_OMNISTUDIO_ENABLED = System.Type.forName('Schema.OmniProcess') != null; - static { // Don't use the org's actual custom metadata records when running tests LoggerConfigurationSelector.useMocks(); @@ -60,6 +58,7 @@ private class LogManagementDataSelector_Tests { Set targetApexClassNames = new Set{ 'SomeClass', 'AnotherClass' }; Integer originalQueryCount = System.Limits.getQueries(); LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryApexClassData', Value__c = String.valueOf(false))); + System.Assert.isFalse(LoggerParameter.QUERY_APEX_CLASS_DATA); List returnedResults = LogManagementDataSelector.getInstance().getApexClasses(targetApexClassNames); @@ -88,6 +87,7 @@ private class LogManagementDataSelector_Tests { Set targetApexTriggerNames = new Set{ 'SomeTrigger', 'AnotherTrigger' }; Integer originalQueryCount = System.Limits.getQueries(); LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryApexTriggerData', Value__c = String.valueOf(false))); + System.Assert.isFalse(LoggerParameter.QUERY_APEX_TRIGGER_DATA); List returnedResults = LogManagementDataSelector.getInstance().getApexTriggers(targetApexTriggerNames); @@ -108,26 +108,6 @@ private class LogManagementDataSelector_Tests { System.Assert.areEqual(expectedResults, returnedResults); } - // FIXME Querying AsyncApexJob in a test context doesn't seem to return results for @future methods, - // and the @future method should be replaced with a queueable class anyway, so fix this test - // when converting the @future method to a private queueable class - // @IsTest - // static void it_returns_count_of_async_apex_jobs_for_specified_class_method_and_statuses() { - // String apexClassName = LogManagementDataSelector_Tests.class.getName(); - // String apexMethodName = 'executeSomeFutureMethod'; - // List jobStatuses = new List{ 'Holding', 'Queued', 'Preparing', 'Processing' }; - // System.Assert.areEqual(0, LogManagementDataSelector.getInstance().getCountOfAsyncApexJobs(apexClassName, apexMethodName, jobStatuses)); - - // System.Test.startTest(); - // executeSomeFutureMethod(); - // System.Test.stopTest(); - - // System.Assert.fail([SELECT status FROM AsyncApexJob]); - // Integer returnedCount = LogManagementDataSelector.getInstance().getCountOfAsyncApexJobs(apexClassName, apexMethodName, jobStatuses); - - // System.Assert.areEqual(1, returnedCount); - // } - @IsTest static void it_returns_cached_recent_log_with_api_release_details_when_call_status_api_callout_is_enabled() { LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'CallStatusApi', Value__c = System.JSON.serialize(true))); @@ -162,6 +142,22 @@ private class LogManagementDataSelector_Tests { System.Assert.areEqual(2, System.Limits.getQueries()); } + @IsTest + static void it_returns_count_of_async_apex_jobs_for_specified_apex_class_and_method_name() { + // Even though an inner class is used, the AsyncApexJob + String apexClassName = LogManagementDataSelector_Tests.class.getName(); + String apexMethodName = null; + List jobStatuses = new List{ 'Completed' }; + System.Assert.areEqual(0, LogManagementDataSelector.getInstance().getCountOfAsyncApexJobs(apexClassName, apexMethodName, jobStatuses)); + + System.Test.startTest(); + System.enqueueJob(new ExampleQueuable()); + System.Test.stopTest(); + + Integer returnedCount = LogManagementDataSelector.getInstance().getCountOfAsyncApexJobs(apexClassName, apexMethodName, jobStatuses); + System.Assert.areEqual(1, returnedCount); + } + @IsTest static void it_returns_null_when_no_recent_log_with_api_release_details_is_found() { LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'CallStatusApi', Value__c = System.JSON.serialize(true))); @@ -238,6 +234,44 @@ private class LogManagementDataSelector_Tests { System.Assert.areEqual(deleteableRecord.Id, returnedResults.get(0).RecordId); } + @IsTest + static void it_does_not_query_flow_definition_view_when_disabled_via_logger_parameter() { + // The IDs used in the query don't particularly matter here - the main concern is checking that the query does not execute at all + List targetFlowDefinitionViewIds = new List{ System.UserInfo.getUserId() }; + Integer originalQueryCount = System.Limits.getQueries(); + LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryFlowDefinitionViewData', Value__c = String.valueOf(false))); + System.Assert.isFalse(LoggerParameter.QUERY_FLOW_DEFINITION_VIEW_DATA); + + List returnedResults = LogManagementDataSelector.getInstance().getFlowDefinitionViewsByFlowApiName(targetFlowDefinitionViewIds); + + System.Assert.areEqual(originalQueryCount, System.Limits.getQueries()); + System.Assert.areEqual(0, returnedResults.size()); + } + + @IsTest + static void it_returns_flow_definition_view_for_nonexistent_api_name() { + LoggerSObjectHandler.shouldExecute(false); + String fakeFlowApiName = 'Some Flow API Name That Cannot Exist Because It Has Spaces and Emojis ๐Ÿ‘ˆ๐Ÿ‘ˆ๐Ÿ˜Ž, THUS, We CAN Safely Call This A Fake API Name'; + + List returnedResults = LogManagementDataSelector.getInstance() + .getFlowDefinitionViewsByFlowApiName(new List{ fakeFlowApiName }); + + System.Assert.isNotNull(returnedResults); + System.Assert.isTrue(returnedResults.isEmpty()); + } + + @IsTest + static void it_returns_flow_version_view_for_nonexistent_durable_ids() { + LoggerSObjectHandler.shouldExecute(false); + String fakeFlowDurableId = 'Some Flow API Name That Cannot Exist Because It Has Spaces and Emojis ๐Ÿ‘ˆ๐Ÿ‘ˆ๐Ÿ˜Ž, THUS, We CAN Safely Call This A Fake API Name'; + + List returnedResults = LogManagementDataSelector.getInstance() + .getFlowVersionViewsByDurableId(new List{ fakeFlowDurableId }); + + System.Assert.isNotNull(returnedResults); + System.Assert.isTrue(returnedResults.isEmpty()); + } + @IsTest static void it_returns_log_for_specified_log_id() { LoggerSObjectHandler.shouldExecute(false); @@ -385,11 +419,7 @@ private class LogManagementDataSelector_Tests { } @IsTest - static void it_returns_omni_processes_for_specified_ids() { - // No need to fail the test if it's running in an org that does not have OmniStudio enabled - if (IS_OMNISTUDIO_ENABLED == false) { - return; - } + static void it_returns_omni_processes_for_empty_list_of_ids() { // Fun fact: in an anonymous Apex script, you can easily create an OmniProcess record... // ...but when you create one in a test class, you get a gack error ๐Ÿฅฒ // Because of this platform limitation, this test is not great - it doesn't create records (because it can't), @@ -398,9 +428,10 @@ private class LogManagementDataSelector_Tests { // TODO revisit to see if there is any other way to create OmniProcess records to improve this test. List omniProcessIds = new List(); - List returnedResults = LogManagementDataSelector.getInstance().getLoggerScenariosById(omniProcessIds); + Map returnedResults = LogManagementDataSelector.getInstance().getOmniProcessProxies(omniProcessIds); System.Assert.isNotNull(returnedResults); + System.Assert.isTrue(returnedResults.isEmpty()); } @IsTest @@ -409,6 +440,7 @@ private class LogManagementDataSelector_Tests { List targetOmniProcessIds = new List{ System.UserInfo.getUserId() }; Integer originalQueryCount = System.Limits.getQueries(); LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryOmniProcessData', Value__c = String.valueOf(false))); + System.Assert.isFalse(LoggerParameter.QUERY_OMNI_PROCESS_DATA); Map returnedResults = LogManagementDataSelector.getInstance().getOmniProcessProxies(targetOmniProcessIds); @@ -585,12 +617,13 @@ private class LogManagementDataSelector_Tests { System.Assert.areEqual(mockSelector, LogManagementDataSelector.getInstance()); } - @SuppressWarnings('PMD.EmptyStatementBlock') - @future - private static void executeSomeFutureMethod() { - // This method intentionally does nothing - it\'s only used to help test queries on AsynxApexJob + private class MockLogManagementDataSelector extends LogManagementDataSelector { } - private class MockLogManagementDataSelector extends LogManagementDataSelector { + private class ExampleQueuable implements System.Queueable { + @SuppressWarnings('PMD.EmptyStatementBlock') + public void execute(System.QueueableContext queueableContext) { + // No-op, just for testing purposes + } } } diff --git a/nebula-logger/core/tests/logger-engine/classes/LoggerEngineDataSelector_Tests.cls b/nebula-logger/core/tests/logger-engine/classes/LoggerEngineDataSelector_Tests.cls index a57613ecb..69e387032 100644 --- a/nebula-logger/core/tests/logger-engine/classes/LoggerEngineDataSelector_Tests.cls +++ b/nebula-logger/core/tests/logger-engine/classes/LoggerEngineDataSelector_Tests.cls @@ -22,7 +22,21 @@ private class LoggerEngineDataSelector_Tests { } @IsTest - static void it_returns_cached_auth_session_proxy() { + static void it_returns_cached_auth_session_proxy_when_populated_in_cache() { + System.Assert.isTrue(LoggerParameter.QUERY_AUTH_SESSION_DATA); + System.Assert.areEqual(0, System.Limits.getQueries()); + LoggerEngineDataSelector.useMocks(); + LoggerSObjectProxy.AuthSession mockAuthSessionProxy = new LoggerSObjectProxy.AuthSession(); + LoggerCache.getOrganizationCache().put('AuthSession' + System.UserInfo.getUserId(), mockAuthSessionProxy); + + LoggerSObjectProxy.AuthSession returnedAuthSessionProxy = LoggerEngineDataSelector.getInstance().getCachedAuthSessionProxy(); + + System.Assert.areEqual(0, System.Limits.getQueries()); + System.Assert.areEqual(mockAuthSessionProxy, returnedAuthSessionProxy); + } + + @IsTest + static void it_returns_queried_auth_session_proxy_when_cache_is_not_populated() { List sessions = [ SELECT Id, @@ -52,6 +66,21 @@ private class LoggerEngineDataSelector_Tests { System.Assert.areEqual(expectedAuthSessionProxy, returnedAuthSessionProxy); } + @IsTest + static void it_returns_mock_auth_session_proxy_when_mock_is_provided() { + System.Assert.isTrue(LoggerParameter.QUERY_AUTH_SESSION_DATA); + System.Assert.areEqual(0, System.Limits.getQueries()); + LoggerEngineDataSelector.useMocks(); + LoggerSObjectProxy.AuthSession mockAuthSessionProxy = new LoggerSObjectProxy.AuthSession(); + mockAuthSessionProxy.UsersId = System.UserInfo.getUserId(); + LoggerEngineDataSelector.mockAuthSessionProxies.add(mockAuthSessionProxy); + + LoggerSObjectProxy.AuthSession returnedAuthSessionProxy = LoggerEngineDataSelector.getInstance().getCachedAuthSessionProxy(); + + System.Assert.areEqual(1, System.Limits.getQueries()); + System.Assert.areEqual(mockAuthSessionProxy, returnedAuthSessionProxy); + } + @IsTest static void it_returns_null_when_querying_auth_session_is_disabled() { LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryAuthSessionData', Value__c = String.valueOf(false))); @@ -77,6 +106,63 @@ private class LoggerEngineDataSelector_Tests { System.Assert.isNull(returnedAuthSessionProxy); } + @IsTest + static void it_returns_null_when_querying_network_is_disabled() { + LoggerParameter.setMock(new LoggerParameter__mdt(DeveloperName = 'QueryNetworkData', Value__c = String.valueOf(false))); + System.Assert.isFalse(LoggerParameter.QUERY_NETWORK_DATA); + Id someNetworkId = System.UserInfo.getUserId(); + System.Assert.areEqual(0, System.Limits.getQueries()); + + LoggerSObjectProxy.Network returnedNetworkProxy = LoggerEngineDataSelector.getInstance().getCachedNetworkProxy(someNetworkId); + + System.Assert.areEqual(0, System.Limits.getQueries()); + System.Assert.isNull(returnedNetworkProxy); + } + + @IsTest + static void it_returns_null_when_network_id_is_null() { + System.Assert.isTrue(LoggerParameter.QUERY_NETWORK_DATA); + Id someNetworkId; + System.Assert.areEqual(0, System.Limits.getQueries()); + + LoggerSObjectProxy.Network returnedNetworkProxy = LoggerEngineDataSelector.getInstance().getCachedNetworkProxy(someNetworkId); + + Integer expectedQueryCount = LoggerEngineDataSelector.IS_EXPERIENCE_CLOUD_ENABLED ? 1 : 0; + System.Assert.areEqual(expectedQueryCount, System.Limits.getQueries()); + System.Assert.isNull(returnedNetworkProxy); + } + + @IsTest + static void it_returns_cached_network_proxy_when_populated_in_cache() { + System.Assert.isTrue(LoggerParameter.QUERY_NETWORK_DATA); + System.Assert.areEqual(0, System.Limits.getQueries()); + LoggerEngineDataSelector.useMocks(); + LoggerSObjectProxy.Network mockNetworkProxy = new LoggerSObjectProxy.Network(); + mockNetworkProxy.Id = System.UserInfo.getUserId(); + LoggerCache.getOrganizationCache().put('Network' + mockNetworkProxy.Id, mockNetworkProxy); + + LoggerSObjectProxy.Network returnedNetworkProxy = LoggerEngineDataSelector.getInstance().getCachedNetworkProxy(mockNetworkProxy.Id); + + System.Assert.areEqual(0, System.Limits.getQueries()); + System.Assert.areEqual(mockNetworkProxy, returnedNetworkProxy); + } + + @IsTest + static void it_returns_mock_network_proxy_when_mock_is_provided() { + System.Assert.isTrue(LoggerParameter.QUERY_NETWORK_DATA); + System.Assert.areEqual(0, System.Limits.getQueries()); + LoggerEngineDataSelector.useMocks(); + LoggerSObjectProxy.Network mockNetworkProxy = new LoggerSObjectProxy.Network(); + mockNetworkProxy.Id = System.UserInfo.getUserId(); + LoggerEngineDataSelector.mockNetworkProxies.add(mockNetworkProxy); + + LoggerSObjectProxy.Network returnedNetworkProxy = LoggerEngineDataSelector.getInstance().getCachedNetworkProxy(mockNetworkProxy.Id); + + Integer expectedQueryCount = LoggerEngineDataSelector.IS_EXPERIENCE_CLOUD_ENABLED ? 1 : 0; + System.Assert.areEqual(expectedQueryCount, System.Limits.getQueries()); + System.Assert.areEqual(mockNetworkProxy, returnedNetworkProxy); + } + @IsTest static void it_returns_cached_organization() { Schema.Organization expectedOrganization = [ diff --git a/sfdx-project.json b/sfdx-project.json index fdb661531..a8e0a9522 100644 --- a/sfdx-project.json +++ b/sfdx-project.json @@ -10,8 +10,8 @@ "definitionFile": "./config/scratch-orgs/base-scratch-def.json", "scopeProfiles": true, "versionNumber": "4.15.2.NEXT", - "versionName": "Improved Testability of Query Selectors", - "versionDescription": "Tweaked the internals of the selector classes to provide more control & reusability to improve overall testability", + "versionName": "Improved Testability of Queries", + "versionDescription": "Enhanced the internals of the existing selector classes LoggerEngineDataSelector and LogManagementDataSelector + introducted a new class LoggerConfigurationSelector to provide improve testability of queries", "releaseNotesUrl": "https://github.com/jongpie/NebulaLogger/releases", "unpackagedMetadata": { "path": "./nebula-logger/extra-tests"