Skip to content

Commit

Permalink
Stabilize Test Runs for Orgs without Trigger Factory Settings and wit…
Browse files Browse the repository at this point in the history
…hout Record Type Filters (#7)

* Stabilize Test Runs for Orgs without Trigger Factory Settings and without Record Type Filters. Make package-specific tests only run during package version creation.

* Increment Package Version after opened Pull Request

* Update Package Version sfdx-project.json, README and Installation Docs

Co-authored-by: Dennis Grzyb <dennis.grzyb@outlook.com>
Co-authored-by: GitHub Action Bot <action@github.com>
  • Loading branch information
3 people authored Mar 9, 2022
1 parent 0faf21b commit 57cc00d
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 29 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
For more information about what the framework can and cannot do, head on over to the [documentation](https://dschibster.github.io/sfdx-trigger-factory)!
# Installation
<div>
<span><a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijd4AAA">
<span><a href="https://login.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijmgAAA">
<img alt="Deploy to Salesforce"
src="https://github.com/dschibster/sfdx-trigger-factory/blob/master/resources/button_install-unlocked-package.png" >
</a>
Expand All @@ -20,7 +20,7 @@ For more information about what the framework can and cannot do, head on over to
<div>
For your Sandbox:
<div><span>
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijd4AAA">
<a href="https://test.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijmgAAA">
<img alt="Deploy to Salesforce"
src="https://github.com/dschibster/ms-triggerframework/blob/master/resources/button_install-unlocked-package.png">
</a></span><div>
Expand Down
4 changes: 2 additions & 2 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

| Environment | Package Type | Install Link |
| ----------- | ------------ | ----------------------------------------------------------------------------------------------- |
| Production | Unlocked | [Click here](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijd4AAA) |
| Sandbox | Unlocked | [Click here](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijd4AAA) |
| Production | Unlocked | [Click here](https://login.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijmgAAA) |
| Sandbox | Unlocked | [Click here](https://test.salesforce.com/packaging/installPackage.apexp?p0=04t09000000ijmgAAA) |
| Production | Unmanaged | Refer to README in Repository |

Simply click on one of the Links to install the App (it's recommended to install the Unlocked Package to easily benefit from future updates).
7 changes: 4 additions & 3 deletions sfdx-project.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"path": "source",
"default": true,
"package": "sfdx-trigger-factory",
"versionName": "ver. 1.2.2",
"versionNumber": "1.2.2.0",
"versionName": "ver. 1.2.3",
"versionNumber": "1.2.3.0",
"unpackagedMetadata": {
"path": "unmanaged"
}
Expand All @@ -23,6 +23,7 @@
"sfdx-trigger-factory@1.1.0-0": "04t09000000ijXKAAY",
"sfdx-trigger-factory@1.2.0-0": "04t09000000ijXeAAI",
"sfdx-trigger-factory@1.2.1-0": "04t09000000ijYXAAY",
"sfdx-trigger-factory@1.2.2-0": "04t09000000ijd4AAA"
"sfdx-trigger-factory@1.2.2-0": "04t09000000ijd4AAA",
"sfdx-trigger-factory@1.2.3-0": "04t09000000ijmgAAA"
}
}
3 changes: 3 additions & 0 deletions source/main/default/classes/TriggerFactoryTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ public with sharing class TriggerFactoryTest {
Map<Id, Lead> leadMap = new Map<Id, Lead>(leadList);

handler.fillTriggerCollections(leadList, new Map<Id, Lead>(leadList), null, null);
handler.setSObjectType(Lead.SObjectType);
handler.triggerContext = TriggerOperation.BEFORE_INSERT;
handler.hasNoRecordsToProcess();
TriggerFactory.execute(handler);
handler.triggerContext = TriggerOperation.AFTER_INSERT;
TriggerFactory.execute(handler);
Expand All @@ -37,6 +39,7 @@ public with sharing class TriggerFactoryTest {

handler.fillTriggerCollections(null, null, leadList, new Map<Id, Lead>(leadList));
handler.triggerContext = TriggerOperation.BEFORE_DELETE;
handler.hasNoRecordsToProcess();
TriggerFactory.execute(handler);
handler.triggerContext = TriggerOperation.AFTER_DELETE;
TriggerFactory.execute(handler);
Expand Down
19 changes: 9 additions & 10 deletions source/main/default/classes/TriggerHandlerExtension.cls
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ public virtual without sharing class TriggerHandlerExtension {
public static Set<SObjectType> disabledObjects = new Set<SObjectType>();
public static Set<String> disabledClasses = new Set<String>();

@TestVisible
private static String RECORDTYPEID_FIELD = 'RecordTypeId';

@TestVisible
private static Map<String, LoopCount> loopCountMap;

public List<SObject> triggerNew = new List<SObject>();
public Map<Id, SObject> triggerNewMap = new Map<Id, SObject>();
public List<SObject> triggerOld = new List<SObject>();
public Map<Id, SObject> triggerOldMap = new Map<Id, SObject>();
@TestVisible
private Set<Id> filteredRecordTypes;

static {
Expand All @@ -24,7 +28,7 @@ public virtual without sharing class TriggerHandlerExtension {
if (Trigger.isExecuting) {
setSObjectType(Trigger.isDelete ? Trigger.old.getSObjectType() : Trigger.new.getSObjectType());
this.isDisabled = isDisabled();
if(isDisabled){
if (isDisabled) {
return;
}
//filter record type here?
Expand Down Expand Up @@ -55,13 +59,8 @@ public virtual without sharing class TriggerHandlerExtension {
this.triggerOldMap = oldMap;
}

@TestVisible
private void filterRecordsByRecordType(String triggerOperation) {
Set<Id> recordTypeIds = TriggerMapping.getInstance().recordTypeFilter.get(getHandlerName());
List<SObject> newListFiltered = new List<SObject>();
Map<Id, SObject> newMapFiltered = new Map<Id, SObject>();
List<SObject> oldListFiltered = new List<SObject>();
Map<Id, SObject> oldMapFiltered = new Map<Id, SObject>();

List<String> operations = triggerOperation.split('_');

switch on operations[1] {
Expand All @@ -82,7 +81,7 @@ public virtual without sharing class TriggerHandlerExtension {
Map<Id, SObject> oldMapNew = new Map<Id, SObject>();

for (SObject sobj : triggerNew) {
if (filteredRecordTypes.contains((Id) sobj.get('RecordTypeId'))) {
if (filteredRecordTypes.contains((Id) sobj.get(RECORDTYPEID_FIELD))) {
newMapNew.put(sobj.Id, sobj);
oldMapNew.put(sobj.Id, triggerOldMap.get(sobj.Id));
}
Expand All @@ -99,7 +98,7 @@ public virtual without sharing class TriggerHandlerExtension {
Map<Id, SObject> newMapNew = new Map<Id, SObject>();

for (SObject sobj : triggerNew) {
if (filteredRecordTypes.contains((Id) sobj.get('RecordTypeId'))) {
if (filteredRecordTypes.contains((Id) sobj.get(RECORDTYPEID_FIELD))) {
newListNew.add(sobj);
if (sobj.Id != null) {
newMapNew.put(sobj.Id, sobj);
Expand All @@ -116,7 +115,7 @@ public virtual without sharing class TriggerHandlerExtension {
Map<Id, SObject> oldMapNew = new Map<Id, SObject>();

for (SObject sobj : triggerOld) {
if (filteredRecordTypes.contains((Id) sobj.get('RecordTypeId'))) {
if (filteredRecordTypes.contains((Id) sobj.get(RECORDTYPEID_FIELD))) {
oldListNew.add(sobj);
oldMapNew.put(sobj.Id, sobj);
}
Expand Down
56 changes: 54 additions & 2 deletions source/main/default/classes/TriggerHandlerExtensionTest.cls
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,20 @@ private class TriggerHandlerExtensionTest {

@isTest
static void testRecordTypeFilter() {
if (
[
SELECT Id
FROM TriggerFactorySetting__mdt
WHERE
(ClassName__c = 'PersonAccountHandler'
AND RecordTypeIds__c != NULL)
OR (ClassName__c = 'BusinessAccountHandler'
AND RecordTypeIds__c != NULL)
]
.size() < 2
) {
return;
}
//First we need to make sure that the correct Record Type Ids are in use, as these will differ on new Scratch Orgs.
Id personAccountRecordTypeId = SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Person_Account').getRecordTypeId();
Id businessAccountRecordTypeId = SObjectType.Account.getRecordTypeInfosByDeveloperName().get('Business_Account').getRecordTypeId();
Expand All @@ -546,10 +560,23 @@ private class TriggerHandlerExtensionTest {
delete acc;
undelete acc;
}

@isTest
static void testCollectionsAreNotFilteredWhenHandlerDisabled() {

if (
[
SELECT Id
FROM TriggerFactorySetting__mdt
WHERE
(ClassName__c = 'PersonAccountHandler'
AND RecordTypeIds__c != NULL)
OR (ClassName__c = 'BusinessAccountHandler'
AND RecordTypeIds__c != NULL)
]
.size() < 2
) {
return;
}
TriggerSettings__c newSettingForUser = new TriggerSettings__c(SetupOwnerId = UserInfo.getUserId(), DisabledObjects__c = 'Account');
insert NewSettingForUser;

Expand Down Expand Up @@ -577,4 +604,29 @@ private class TriggerHandlerExtensionTest {
delete acc;
undelete acc;
}

@IsTest
static void mockRecordTypeFilterWithoutRecordId() {
TriggerMapping.getInstance().mappedHandlers.put('LEAD', new List<String>{ 'TriggerHandlerExtension' });
List<TriggerHandlerExtension> triggerHandlers = TriggerFactory.getHandlers(Lead.SObjectType);
TriggerHandlerExtension handler = triggerHandlers[0];

Lead testLead = new Lead(LastName = 'Test', Company = 'mindsquare');
List<Lead> leadList = new List<Lead>{ testLead };
insert leadList;
Map<Id, Lead> leadMap = new Map<Id, Lead>(leadList);

TriggerMapping.getInstance().recordTypeFilter.put('TriggerHandlerExtension', new Set<Id>(new List<Id>{ testLead.Id }));
TriggerHandlerExtension.RECORDTYPEID_FIELD = 'Id';
handler.filteredRecordTypes = new Set<Id>(new List<Id>{ testLead.Id });
handler.setSObjectType(Lead.SObjectType);
handler.fillTriggerCollections(leadList, new Map<Id, Lead>(leadList), null, null);
handler.filterRecordsByRecordType('AFTER_INSERT');

handler.fillTriggerCollections(null, null, leadList, new Map<Id, Lead>(leadList));
handler.filterRecordsByRecordType('AFTER_DELETE');

handler.fillTriggerCollections(leadList, new Map<Id, Lead>(leadList), leadList, new Map<Id, Lead>(leadList));
handler.filterRecordsByRecordType('BEFORE_UPDATE');
}
}
23 changes: 17 additions & 6 deletions source/main/default/classes/TriggerMapping.cls
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ public without sharing class TriggerMapping {
public Map<String, List<String>> mappedHandlers;
public Map<String, Set<Id>> recordTypeFilter;

@TestVisible
static List<TriggerFactorySetting__mdt> triggerFactorySettings {
get {
if (triggerFactorySettings == null) {
triggerFactorySettings = [
SELECT SObjectName__c, ClassName__c, RecordTypeIds__c
FROM TriggerFactorySetting__mdt
WHERE IsDisabled__c = FALSE
ORDER BY SObjectName__c, OrderOfExecution__c
];
}
return triggerFactorySettings;
}
set;
}

/**
* @description Single Design Pattern method for retrieval of Trigger Mappings
* Retrieves Trigger Factory Settings and groups the Trigger Handlers to execute based on the Object name and their Order of Execution.
Expand All @@ -20,12 +36,7 @@ public without sharing class TriggerMapping {
private void load() {
mappedHandlers = new Map<String, List<String>>();
recordTypeFilter = new Map<String, Set<Id>>();
for (TriggerFactorySetting__mdt setting : [
SELECT SObjectName__c, ClassName__c, RecordTypeIds__c
FROM TriggerFactorySetting__mdt
WHERE IsDisabled__c = FALSE
ORDER BY SObjectName__c, OrderOfExecution__c
]) {
for (TriggerFactorySetting__mdt setting : triggerFactorySettings) {
if (!mappedHandlers.containsKey(setting.SObjectName__c.toUpperCase())) {
mappedHandlers.put(setting.SObjectName__c.toUpperCase(), new List<String>());
}
Expand Down
18 changes: 14 additions & 4 deletions source/main/default/classes/TriggerMappingTest.cls
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
@isTest
public with sharing class TriggerMappingTest {
private static String factoryDummyValues = '[{"Label":"Account_Person","MasterLabel":"Account_Person","ClassName__c":"PersonAccountHandler","Language":"en_US","RecordTypeIds__c":"0121X000003twHvQAI","IsDisabled__c":false,"QualifiedApiName":"Account_Person","SObjectName__c":"Account","Id":"m005E0000002NLS","OrderOfExecution__c":1,"DeveloperName":"Account_Person","NamespacePrefix":null}]';
@isTest
static void getTriggerMap() {
//It is a given that we have at least one Custom Metadata Type in the Trigger Factory Setting (e.g. the Dummy Object from this pkg)
//Therefore we only need to initialize the Map
//Since we got rid of unmanaged metadata in our package, we are now verifying the existence of mapped Handlers by mocking an active Handler with a Record Type Filter.
TriggerMapping.triggerFactorySettings = (List<TriggerFactorySetting__mdt>) JSON.deserialize(factoryDummyValues, List<TriggerFactorySetting__mdt>.class);

TriggerMapping trigMap = TriggerMapping.getInstance();
System.assert(trigMap.mappedHandlers.size() > 0);
System.assert(trigMap.mappedHandlers.size() == 1);

//DummyObject2 was previously part of the unmanaged metadata, but is auto-excluded here because it does not come up in a query where IsDisabled__c = false

//Additionally DummyObject2 should not come up in this instance, because it is disabled.
System.assert(!trigMap.mappedHandlers.containsKey('DUMMYOBJECT2'));

System.assert(trigMap.recordTypeFilter.containsKey('PersonAccountHandler'));
}

@isTest
static void getTriggerMapWhenTriggerFactorySettingsExist() {
if ([SELECT COUNT() FROM TriggerFactorySetting__mdt] != 0) {
TriggerMapping.getInstance();
}
}
}

0 comments on commit 57cc00d

Please sign in to comment.