From 918873c916155412010bd1b3e9208d87ded0c84a Mon Sep 17 00:00:00 2001 From: Robert Baillie Date: Fri, 3 Dec 2021 14:15:54 +0000 Subject: [PATCH] Removed the ortoo_FabricatedSObject - have to accept that the library is bespoke Added tests for the DirectedGraph and the new ObjectDescriber methods --- .../default/classes/utils/DirectedGraph.cls | 2 + .../classes/utils/tests/DirectedGraphTest.cls | 184 ++++++++++++++++++ .../tests/DirectedGraphTest.cls-meta.xml} | 2 +- .../classes/ortoo_FabricatedSObject.cls | 26 --- .../ortoo_FabricatedSObjectRegister.cls | 11 +- ...too_FabricatedSObjectRegister.cls-meta.xml | 0 .../classes/sfab_FabricatedSObject.cls | 12 +- .../tests/sfab_ObjectDescriberTest.cls | 49 +++++ 8 files changed, 248 insertions(+), 38 deletions(-) create mode 100644 framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls rename framework/default/{sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls-meta.xml => ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml} (80%) delete mode 100644 framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls rename framework/default/{sobject-fabricator-extensions => sobject-fabricator}/classes/ortoo_FabricatedSObjectRegister.cls (89%) rename framework/default/{sobject-fabricator-extensions => sobject-fabricator}/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml (100%) diff --git a/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls index 18a98d2f4a0..4c8f8736d0a 100644 --- a/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls +++ b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls @@ -31,6 +31,8 @@ public inherited sharing class DirectedGraph */ public DirectedGraph addRelationship( Object child, Object parent ) { + Contract.requires( parentsByChildren.containsKey( child ), 'addRelationship called with a child that has not been added as a node (' + child + ')' ); + Contract.requires( parentsByChildren.containsKey( parent ), 'addRelationship called with a parent that has not been added as a node (' + parent + ')' ); parentsByChildren.get( child ).add( parent ); return this; } diff --git a/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls new file mode 100644 index 00000000000..3dad7d1becc --- /dev/null +++ b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls @@ -0,0 +1,184 @@ +@isTest +private without sharing class DirectedGraphTest +{ + @isTest + private static void generateSorted_whenASimpleGraphIsSpecified_willReturnTheNodesInOrder() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 'Great grandparent' ) + .addNode( 'Grandparent' ) + .addNode( 'Parent' ) + .addNode( 'Child' ) + .addRelationship( 'Child', 'Parent' ) + .addRelationship( 'Parent', 'Grandparent') + .addRelationship( 'Grandparent', 'Great grandparent' ); + + List expectedNodes = new List + { + 'Child', + 'Parent', + 'Grandparent', + 'Great grandparent' + }; + + List returnedNodes = graph.generateSorted(); + + System.assertEquals( expectedNodes, returnedNodes, 'generateSorted, when a simple graph has been built, will return the nodes in child to parent order' ); + } + + @isTest + private static void generateSorted_whenAComplexGraphIsSpecified_willReturnTheNodesInOrder() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 'Great grandparent' ) + .addNode( 'Grandparent of both parents' ) + .addNode( 'Parent 1' ) + .addNode( 'Parent 2' ) + .addNode( 'Child 1 of Parent 1' ) + .addNode( 'Child 2 of Parent 1' ) + .addNode( 'Child 1 of Parent 2' ) + .addNode( 'Child 2 of Parent 2' ) + .addNode( 'Child of Parents 1 and 2' ) + + .addRelationship( 'Grandparent of both parents', 'Great grandparent' ) + .addRelationship( 'Parent 1', 'Grandparent of both parents' ) + .addRelationship( 'Parent 2', 'Grandparent of both parents' ) + .addRelationship( 'Child 1 of Parent 1', 'Parent 1' ) + .addRelationship( 'Child 2 of Parent 1', 'Parent 1' ) + .addRelationship( 'Child 1 of Parent 2', 'Parent 2' ) + .addRelationship( 'Child 2 of Parent 2', 'Parent 2' ) + .addRelationship( 'Child of Parents 1 and 2', 'Parent 1') + .addRelationship( 'Child of Parents 1 and 2', 'Parent 2' ); + + List expectedNodes = new List + { + 'Child 1 of Parent 1', + 'Child 2 of Parent 1', + 'Child 1 of Parent 2', + 'Child 2 of Parent 2', + 'Child of Parents 1 and 2', + 'Parent 1', + 'Parent 2', + 'Grandparent of both parents', + 'Great grandparent' + }; + + List returnedNodes = graph.generateSorted(); + + System.assertEquals( expectedNodes, returnedNodes, 'generateSorted, when a complex graph has been built, will return the nodes in child to parent order' ); + } + + @isTest + private static void generateSorted_whenNoGraphIsSpecified_willReturnAnEmptyList() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph(); + List expectedNodes = new List(); + + List returnedNodes = graph.generateSorted(); + + System.assertEquals( expectedNodes, returnedNodes, 'generateSorted, when no graph has been built, will return the an empty list' ); + } + + @isTest + private static void generateSorted_whenADuplicatedNodesAreSpecified_willReturnTheUniqueNodesInOrder() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 'Great grandparent' ) + .addNode( 'Great grandparent' ) + .addNode( 'Great grandparent' ) + .addNode( 'Grandparent' ) + .addNode( 'Grandparent' ) + .addNode( 'Grandparent' ) + .addNode( 'Parent' ) + .addNode( 'Parent' ) + .addNode( 'Child' ) + .addNode( 'Child' ) + .addNode( 'Child' ) + .addNode( 'Child' ) + .addNode( 'Child' ) + .addRelationship( 'Child', 'Parent' ) + .addRelationship( 'Child', 'Parent' ) + .addRelationship( 'Child', 'Parent' ) + .addRelationship( 'Child', 'Parent' ) + .addRelationship( 'Parent', 'Grandparent') + .addRelationship( 'Parent', 'Grandparent') + .addRelationship( 'Grandparent', 'Great grandparent' ); + + List expectedNodes = new List + { + 'Child', + 'Parent', + 'Grandparent', + 'Great grandparent' + }; + + List returnedNodes = graph.generateSorted(); + + System.assertEquals( expectedNodes, returnedNodes, 'generateSorted, when duplicate nodes and relationships are specified, will return the unique nodes in child to parent order' ); + } + + @isTest + private static void generateSorted_whenACircularReference_willThrowAnException() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 1 ) + .addNode( 2 ) + .addNode( 3 ) + .addRelationship( 1, 2 ) + .addRelationship( 2, 3 ) + .addRelationship( 3, 1 ); + Test.startTest(); + String exceptionMessage; + try + { + graph.generateSorted(); + } + catch ( Exception e ) + { + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + Amoss_Asserts.assertContains( 'The graph contains a circular reference and therefore cannot be resolved', exceptionMessage, 'generateSorted, when a circular reference has been defined, will throw an exception' ); + } + + @isTest + private static void addRelationship_whenGivenAnInvalidChild_willThrowAnException() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 'Parent' ); + Test.startTest(); + String exceptionMessage; + try + { + graph.addRelationship( 'UnregisteredChild', 'Parent' ); + } + catch ( Exception e ) + { + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + Amoss_Asserts.assertContains( 'addRelationship called with a child that has not been added as a node (UnregisteredChild)', exceptionMessage, 'addRelationship, when given a child that has not previously been added, will throw an exception' ); + } + + @isTest + private static void addRelationship_whenGivenAnInvalidParent_willThrowAnException() // NOPMD: Test method name format + { + DirectedGraph graph = new DirectedGraph() + .addNode( 'Child' ); + Test.startTest(); + String exceptionMessage; + try + { + graph.addRelationship( 'Child', 'UnregisteredParent' ); + } + catch ( Exception e ) + { + exceptionMessage = e.getMessage(); + } + Test.stopTest(); + + Amoss_Asserts.assertContains( 'addRelationship called with a parent that has not been added as a node (UnregisteredParent)', exceptionMessage, 'addRelationship, when given a parent that has not previously been added, will throw an exception' ); + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls-meta.xml b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml similarity index 80% rename from framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls-meta.xml rename to framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml index f928c8e56bc..dd61d1f917e 100644 --- a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls-meta.xml +++ b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml @@ -1,5 +1,5 @@ - 53.0 + 52.0 Active diff --git a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls b/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls deleted file mode 100644 index 90cb67b8688..00000000000 --- a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObject.cls +++ /dev/null @@ -1,26 +0,0 @@ -public virtual class ortoo_FabricatedSObject extends sfab_FabricatedSObject -{ - public ortoo_FabricatedSObject( Type sType ) - { - super( sType ); - ortoo_FabricatedSObjectRegister.registerObject( this ); - } - - public List getParentRelationshipNodes() - { - List parentNodes = new List(); - for ( sfab_FabricatedSObjectNode thisNode : nodes.values() ) - { - if ( thisNode instanceOf sfab_ParentRelationshipNode ) - { - parentNodes.add( (sfab_ParentRelationshipNode)thisNode ); - } - } - return parentNodes; - } - - public SobjectType getSobjectType() - { - return SobjectUtils.getSobjectType( getSobjectName() ); - } -} \ No newline at end of file diff --git a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObjectRegister.cls b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls similarity index 89% rename from framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObjectRegister.cls rename to framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls index d3cb8ca4e1c..285e353ba4c 100644 --- a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObjectRegister.cls +++ b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls @@ -9,10 +9,7 @@ public class ortoo_FabricatedSObjectRegister { public static void registerObject( sfab_FabricatedSObject objectToRegister ) { objectRegister.add( objectToRegister ); - if ( objectToRegister instanceof ortoo_FabricatedSobject ) - { - graph.addNode( ((ortoo_FabricatedSobject)objectToRegister).getSobjectType() ); - } + graph.addNode( objectToRegister.getSobjectType() ); } public static void registerChildOfRelationship( sfab_FabricatedSObject child, String relationship, sfab_FabricatedSObject parent ) @@ -82,11 +79,7 @@ public class ortoo_FabricatedSObjectRegister { this.parent = parent; this.relationship = relationship; this.child = child; - - if ( child instanceOf ortoo_FabricatedSobject && parent instanceOf ortoo_FabricatedSobject ) - { - graph.addRelationship( ((ortoo_FabricatedSobject)child).getSobjectType(), ((ortoo_FabricatedSobject)parent).getSobjectType() ); - } + graph.addRelationship( child.getSobjectType(), parent.getSobjectType() ); } public void register( ortoo_SobjectUnitOfWork uow ) diff --git a/framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml similarity index 100% rename from framework/default/sobject-fabricator-extensions/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml rename to framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml diff --git a/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls index b2291f2d20b..0b2ce4d734c 100644 --- a/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls +++ b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls @@ -26,6 +26,7 @@ public virtual class sfab_FabricatedSObject { */ public sfab_FabricatedSObject( Type sType ) { this.sType = sType; + ortoo_FabricatedSObjectRegister.registerObject( this ); } /** @@ -340,12 +341,19 @@ public virtual class sfab_FabricatedSObject { return String.valueOf( sType ); } + // TODO: document + // TODO: test + public SobjectType getSobjectType() + { + return SobjectUtils.getSobjectType( getSobjectName() ); + } + /** * Given a field name, will split it into its parent and child references, checking that the parent relatioonship * exists and defaulting the field to an empty version of the appropriate object. * - * @param String - The name of the field to split and defaul - * @return Map - The + * @param String - The name of the field to split and default + * @return sfab_FieldNameSplitter */ private sfab_FieldNameSplitter defaultParentField( String fieldName ) { diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls index cd1db675bc4..452adfa3a56 100644 --- a/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls +++ b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls @@ -271,4 +271,53 @@ public with sharing class sfab_ObjectDescriberTest { .getObjectTypeForChildRelationship( 'Invalid', 'Account' ); System.assertEquals( null, targetObjectType ); } + + @isTest + private static void getFieldForChildRelationship_whenGivenValidNames_expectTheField() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForChildRelationship( 'Account', 'Contacts' ); + System.assertEquals( Contact.AccountId, field ); + } + + @isTest + private static void getFieldForChildRelationship_whenGivenAnInvalidRelationship_expectNull() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForChildRelationship( 'Account', 'ContactLike' ); + System.assertEquals( null, field ); + } + + @isTest + private static void getFieldForChildRelationship_whenGivenAnInvalidObject_expectNull() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForChildRelationship( 'Accountant', 'Contacts' ); + System.assertEquals( null, field ); + } + + + @isTest + private static void getFieldForParentRelationship_whenGivenValidNames_expectTheField() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForParentRelationship( 'Contact', 'Account' ); + System.assertEquals( Contact.AccountId, field ); + } + + @isTest + private static void getFieldForParentRelationship_whenGivenAnInvalidRelationship_expectNull() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForParentRelationship( 'Contact', 'Accooooooo' ); + System.assertEquals( null, field ); + } + + @isTest + private static void getFieldForParentRelationship_whenGivenAnInvalidObject_expectNull() // NOPMD: Test method name format + { + SObjectField field = new sfab_ObjectDescriber() + .getFieldForParentRelationship( 'Accountant', 'Account' ); + System.assertEquals( null, field ); + } } \ No newline at end of file