diff --git a/TODO.txt b/TODO.txt index 33478240684..8126376cab6 100644 --- a/TODO.txt +++ b/TODO.txt @@ -2,8 +2,7 @@ Licenses that are needed with the source code and binary: * fflib - https://github.com/apex-enterprise-patterns/fflib-apex-common/blob/master/LICENSE * fflib apex extensions - https://github.com/wimvelzeboer/fflib-apex-extensions/blob/main/LICENSE * Amoss - https://github.com/bobalicious/amoss/blob/main/LICENSE - -TODO: +* SObject Fabricator - https://github.com/bobalicious/SObjectFabricator/blob/master/LICENSE Look at the use of 'MockDatabase' in fflib @@ -31,6 +30,23 @@ Add to documentation * Using the Mock Registarar * Describe the Application Factories +From Utilities, things that may be useful: +* getReferenceObjectAPIName +* getObjName - get the object name from an Id +* getLabel / getObjectLabel - get the label for an sobject +* getFieldLabel +* delimitedStringToSet and reverse + * escaping single quotes - in both directions? +* unitsBetweenDateTime +* emailAddressIsValid / emailAddressListIsValid +* sObjectIsCustom / sObjectIsCustomfromAPIName +* IsfieldFilterable +* isFieldCustom +* idIsValid +* getCrossObjectAPIName +* objectFieldExist +* sortSelectOptions - complete re-write + Write tests for the SOQL generation in the criteria library Amoss_Asserts.assertContains improvement into the OS lib diff --git a/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls new file mode 100644 index 00000000000..4c8f8736d0a --- /dev/null +++ b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls @@ -0,0 +1,146 @@ +// Directed Graph algorithm ased on the DirectedGraph implemented by Robert Sösemann: https://github.com/rsoesemann/apex-domainbuilder +public inherited sharing class DirectedGraph +{ + public inherited sharing class GraphContainsCircularReferenceException extends ortoo_Exception {} + // + // TODO: detect and block circular references. We can deal with them independently + // + private Map> parentsByChildren = new Map>(); + + /** + * Adds a node to the graph + * + * @param Object The node to add + * @return DirectedGraph Itself, allowing for a fluent interface + */ + public DirectedGraph addNode( Object node ) + { + if( ! parentsByChildren.containsKey( node ) ) + { + parentsByChildren.put( node, new Set() ); + } + return this; + } + + /** + * Adds a relationship between two nodes + * + * @param Object The child node of the relationship + * @param Object The parent node of the relationship + * @return DirectedGraph Itself, allowing for a fluent interface + */ + 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; + } + + /** + * Generates a list of nodes, sorted by their depdencies. + * + * That is, the children first, resolving upwards to the parents. + * No parent appears in the list prior to any of their children. + * + * Algorithm: + * A leaf node is added + * All references to that as a child are removed + * If any parent no longer has any children registered, it is regarded as a leaf node + * Move onto the next leaf node. + * + * Assuming that there are no circular references, + * Eventually, every node will be regarded as a leaf node, and therefore every node will be added + * + * @param Object The child node of the relationship + * @param Object The parent node of the relationship + * @return DirectedGraph Itself, allowing for a fluent interface + */ + public List generateSorted() + { + List sortedObjects = new List(); + + while( ! leafNodes.isEmpty() ) + { + Object currentLeaf = (Object)leafNodes.iterator().next(); + leafNodes.remove( currentLeaf ); + + sortedObjects.add( currentLeaf ); + + for( Object thisParent : parentsByChildren.get( currentLeaf ) ) + { + if ( childCountsByParents.containsKey( thisParent ) ) + { + Integer remainingChildrenCount = childCountsByParents.get( thisParent ) - 1; + childCountsByParents.put( thisParent, remainingChildrenCount ); + + if ( remainingChildrenCount == 0 ) + { + leafNodes.add( thisParent ); + } + } + } + } + + leafNodes = null; // reset the leaf nodes so they will be re-calculated on a subsequent call + childCountsByParents = null; // similar to above + + if ( sortedObjects.size() != allNodes.size() ) + { + throw new GraphContainsCircularReferenceException( 'The graph contains a circular reference and therefore cannot be resolved.' ); + } + return sortedObjects; + } + + /** + * A reference to the full list of nodes registered on this graph. + */ + private Set allNodes + { + get + { + return parentsByChildren.keySet(); + } + } + + private Set leafNodes + { + get + { + if ( leafNodes == null ) + { + leafNodes = new Set(); + leafNodes.addAll( allNodes ); + leafNodes.removeAll( childCountsByParents.keySet() ); + } + return leafNodes; + } + set; + } + + private Map childCountsByParents + { + get + { + if ( childCountsByParents == null ) + { + childCountsByParents = new Map(); + + for ( Object thisChild : allNodes ) + { + for ( Object parent : parentsByChildren.get( thisChild ) ) + { + if ( ! childCountsByParents.containsKey( parent ) ) + { + childCountsByParents.put( parent, 0 ); + } + childCountsByParents.put( parent, childCountsByParents.get( parent ) + 1 ); + } + } + + } + return childCountsByParents; + } + set; + } +} diff --git a/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls-meta.xml b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/ortoo-core/default/classes/utils/DirectedGraph.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + 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/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml new file mode 100644 index 00000000000..dd61d1f917e --- /dev/null +++ b/framework/default/ortoo-core/default/classes/utils/tests/DirectedGraphTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 52.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls new file mode 100644 index 00000000000..d770da338e0 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls @@ -0,0 +1,190 @@ +/** + * Provides the ability for the FabricatedSobject class to register objects so that they then can be persisted. + * + * Is an extension to sfab_FabricatedSobject, which does not provide persistence. + * + */ +public class ortoo_FabricatedSObjectRegister { + + /** + * Fabricate and persist all Sobjects that have been registered. + */ + public static void persist() + { + instance.persist(); + } + + /** + * Register the given fabricated SObject so it is eligible to be persisted. + * + * Note, this method is internal to the fabricator library and should not be called directly. + * + * @param sfab_FabricatedSObject The object to register + */ + public static void registerObject( sfab_FabricatedSObject objectToRegister ) + { + instance.registerObject( objectToRegister ); + } + + /** + * Register a relationship between a child and a parent fabricated SObject by stating the relationship + * from the child's perspective. + * E.g. contact, 'Account', account + * + * Note, this method is internal to the fabricator library and should not be called directly. + * + * @param sfab_FabricatedSObject The child object + * @param String The name of the relationship from the child's perspective + * @param sfab_FabricatedSObject The parent object + */ + public static void registerChildOfRelationship( sfab_FabricatedSObject child, String relationship, sfab_FabricatedSObject parent ) + { + instance.registerChildOfRelationship( child, relationship, parent ); + } + + /** + * Register a relationship between a child and a parent fabricated SObject by stating the relationship + * from the parent's perspective. + * E.g. account, 'Contacts', contact + * + * Note, this method is internal to the fabricator library and should not be called directly. + * + * @param sfab_FabricatedSObject The parent object + * @param String The name of the relationship from the parent's perspective + * @param sfab_FabricatedSObject The child object + */ + public static void registerParentOfRelationship( sfab_FabricatedSObject parent, String relationship, sfab_FabricatedSObject child ) + { + instance.registerParentOfRelationship( parent, relationship, child ); + } + + @testVisible + private static RegisterInstance instance + { + get + { + if ( instance == null ) + { + instance = new RegisterInstance(); + } + return instance; + } + set; + } + + @testVisible + private inherited sharing class RegisterInstance + { + private List objectRegister = new List(); + private List relationships = new List(); + private Map sobjectsByFabricated; + private DirectedGraph graph = new DirectedGraph(); + + public void registerObject( sfab_FabricatedSObject objectToRegister ) + { + objectRegister.add( objectToRegister ); + graph.addNode( objectToRegister.getSobjectType() ); + } + + public void registerChildOfRelationship( sfab_FabricatedSObject child, String relationship, sfab_FabricatedSObject parent ) + { + relationships.add( + buildChildOfRelationship( child, relationship, parent ) + ); + graph.addRelationship( child.getSobjectType(), parent.getSobjectType() ); + } + + public void registerParentOfRelationship( sfab_FabricatedSObject parent, String relationship, sfab_FabricatedSObject child ) + { + relationships.add( + buildParentOfRelationship( parent, relationship, child ) + ); + graph.addRelationship( child.getSobjectType(), parent.getSobjectType() ); + } + + public void persist() + { + ortoo_SObjectUnitOfWork uow = (ortoo_SObjectUnitOfWork)Application.UNIT_OF_WORK.newInstance( getOrderOfInserts() ); + + buildObjectsByFabricated(); + registerInserts( uow ); + registerRelationships( uow ); + uow.commitWork(); + } + + @testVisible + private List getOrderOfInserts() + { + List childToParentTypes = graph.generateSorted(); + + List parentToChildTypes = new List(); + for( Integer i = childToParentTypes.size() - 1; i >= 0; i-- ) + { + parentToChildTypes.add( (SobjectType)childToParentTypes[i] ); + } + return parentToChildTypes; + } + + private void buildObjectsByFabricated() + { + sobjectsByFabricated = new Map(); + for ( sfab_FabricatedSObject thisFabricatedObject : objectRegister ) + { + Sobject objectToStore = thisFabricatedObject.toPersistableSobject(); + sobjectsByFabricated.put( thisFabricatedObject, objectToStore ); + } + } + + private void registerInserts( ortoo_SobjectUnitOfWork uow ) + { + for ( sfab_FabricatedSObject thisFabricatedObject : objectRegister ) + { + uow.registerNew( sobjectsByFabricated.get( thisFabricatedObject ) ); + } + } + + private void registerRelationships( ortoo_SobjectUnitOfWork uow ) + { + for ( Relationship thisRelationship : relationships ) + { + thisRelationship.register( uow, sobjectsByFabricated ); + } + } + } + + private inherited sharing class Relationship + { + sfab_FabricatedSObject child; + sfab_FabricatedSObject parent; + SobjectField relationship; + + public Relationship( sfab_FabricatedSObject child, SobjectField relationship, sfab_FabricatedSObject parent ) + { + this.parent = parent; + this.relationship = relationship; + this.child = child; + } + + public void register( ortoo_SobjectUnitOfWork uow, Map sobjectsByFabricated ) + { + Sobject parentSobject = sobjectsByFabricated.get( parent ); + Sobject childSobject = sobjectsByFabricated.get( child ); + + uow.registerRelationship( childSobject, relationship, parentSobject ); + } + } + + // Would be on Relationship if inner classes were allowed to have static methods + private static Relationship buildChildOfRelationship( sfab_FabricatedSObject child, String relationship, sfab_FabricatedSObject parent ) + { + SobjectField relationshipField = new sfab_ObjectDescriber().getFieldForParentRelationship( child.getSobjectName(), relationship ); + return new Relationship( child, relationshipField, parent ); + } + + // Would be on Relationship if inner classes were allowed to have static methods + private static Relationship buildParentOfRelationship( sfab_FabricatedSObject parent, String relationship, sfab_FabricatedSObject child ) + { + SobjectField relationshipField = new sfab_ObjectDescriber().getFieldForChildRelationship( parent.getSobjectName(), relationship ); + return new Relationship( child, relationshipField, parent ); + } +} diff --git a/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/ortoo_FabricatedSObjectRegister.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls new file mode 100644 index 00000000000..ce1c455d7c6 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls @@ -0,0 +1,894 @@ +public virtual class sfab_FabricatedSObject { + + public interface sfab_FabricatedSObjectNode { + Map serialize( Boolean persistable ); + void postBuildProcess( Object objectToProcess ); + String getName(); + } + + private Type sType; + @testVisible protected Map nodes = new Map(); + + sfab_ObjectDescriber objectDescriber = new sfab_ObjectDescriber(); + + public class FieldDoesNotExistException extends Exception {} + public class ParentRelationshipDoesNotExistException extends Exception {} + public class ChildRelationshipDoesNotExistException extends Exception {} + + public class FieldIsNotSimpleFieldException extends Exception {} + public class FieldIsNotParentRelationshipException extends Exception {} + public class FieldIsNotChildRelationshipException extends Exception {} + + public class FieldIsADifferentTypeException extends Exception {} + + public class ParentRelationshipObjectCannotBeAutoCreatedException extends Exception {} + + public class NodeNotSetException extends Exception {} + + /** + * Constructs a FabricatedSObject of the given type. + * + * @param Type - The type of SObject to be fabricated (e.g. Contact.class) + */ + public sfab_FabricatedSObject( Type sType ) { + this.sType = sType; + ortoo_FabricatedSObjectRegister.registerObject( this ); + } + + /** + * Constructs a FabricatedSObject of the given type, and then sets the fields specified in the given map. + * + * @param Type - The type of SObject to be fabricated (e.g. Contact.class) + * @param Map - The fields to set on this object, with the desired values + */ + public sfab_FabricatedSObject( Type sType, Map fields ) { + this(sType); + set(fields); + } + + /** + * Constructs a FabricatedSObject of the given type, and then sets the fields and relationships specified in the given map. + * + * Example valid field / relationship names: + * * Name + * * Account + * * Account.Name + * * Opportunities + * * Account.Opportunities + * * Account.Owner.Contact.FirstName + * + * @param Type - The type of SObject to be fabricated (e.g. Contact.class) + * @param Map - The fields and relationships to set on this object, with the desired values + */ + public sfab_FabricatedSObject(Type sType, Map fields) { + this(sType); + set(fields); + } + + @testVisible + protected sfab_FabricatedSObject( Type sType, List nodes ) { + this.sType = sType; + this.nodes = new Map(); + for ( sfab_FabricatedSObjectNode node : nodes ) { + setNode( node.getName(), node ); + } + } + + /** + * Set the specified field to the specified value. + * + * Note: Only fields on this object's type may be set. Only the 'name' of the field is used and expected results may + * occur if you attempt to pass a field on a different object. + * + * @param Schema.SObjectField - The field to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject set( Schema.SObjectField field, Object value) { + return setField( field, value ); + } + + /** + * Set the specified field / relationship to the specified value. + * + * Example valid field / relationship names: + * * Name + * * Account + * * Account.Name + * * Opportunities + * * Account.Opportunities + * * Account.Owner.Contact.FirstName + * + * @param String - The field or relationship to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject set( String fieldName, Object value ) { + + // This looks like it duplicates the overloading of 'set', but unfortunately Apex determines the method to call + // based on the static type of a parameter rather than the dynamic type. + // I.E. if a passed parameter's value is declared as 'Object' it will always call the 'Object' + // defined version of an overloaded method even when a more specific type matches for a given value. + // This means that 'setParentField' will always call this version of the overloaded method regardless + // of the contents of the value parameter. So we need to manually check the instance type in the code. + if ( value instanceOf List ) { + return setChildren( fieldName, (List)value ); + } + + if ( value instanceOf sfab_FabricatedSObject ) { + return setParent( fieldName, (sfab_FabricatedSObject)value ); + } + + return setField( fieldName, value ); + } + + /** + * Set the specified parent relationship to the specified sfab_FabricatedSObject + * + * Is a more specific version of set( String fieldName, Object value ) + * Is a synonym of setParent( String relationshipName, sfab_FabricatedSObject fabricatedParent ) + * + * @param String - The relationship to set + * @param sfab_FabricatedSObject - The object that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject set( String relationshipName, sfab_FabricatedSObject fabricatedParent ) { + return setParent( relationshipName, fabricatedParent ); + } + + /** + * Set the specified child relationship to the specified list of sfab_FabricatedSObject + * + * Is a more specific version of set( String fieldName, Object value ) + * Is a synonym of setChildren( String relationshipName, List fabricatedChildren ) + * + * @param String - The relationship to set + * @param List - The objects that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject set( String relationshipName, List fabricatedChildren ) { + return setChildren( relationshipName, fabricatedChildren ); + } + + /** + * Add the specified sfab_FabricatedSObject to the specified child relationship, setting it as an empty list to start + * if it does not already exist + * + * Is a synonym of addChild( String relationshipName, sfab_FabricatedSObject fabricatedChild ) + * + * @param String - The relationship to set + * @param sfab_FabricatedSObject - The object to add to the child relationship's list + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject add( String relationshipName, sfab_FabricatedSObject fabricatedChild ) { + return addChild( relationshipName, fabricatedChild ); + } + + /** + * Set the specified field, which exists directly on this object, to the specified value. + * + * @param Schema.SObjectField - The field to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject set( Map fields ) { + for ( Schema.SObjectField field : fields.keySet() ) { + setField( field, fields.get( field ) ); + } + return this; + } + + /** + * Set the specified fields and relationships to the specified values. + * + * Example valid field / relationship names: + * * Name + * * Account + * * Account.Name + * * Opportunities + * * Account.Opportunities + * * Account.Owner.Contact.FirstName + * + * @param Map - The fields / relationships to set (indexes), and the values to set them to. + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + * + */ + public sfab_FabricatedSObject set( Map fields ) { + for (String fieldName : fields.keySet()) { + Object value = fields.get( fieldName ); + set( fieldName, fields.get(fieldName) ); + } + return this; + } + + /** + * Set the specified field, which exists directly on this object, to the specified value. + * + * Is a synonym of set( Schema.SObjectField field, Object value ) + * + * @param Schema.SObjectField - The field to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject setField( Schema.SObjectField field, Object value ) { + return setDirectField( field.getDescribe().getName(), value ); + } + + /** + * Set the specified field, which may be directly on this object or on a parent, to the specified value. + * + * Is a more specific version of set( String fieldName, Object value ) + * + * @param String - The field to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject setField( String fieldName, Object value ) { + if ( new sfab_FieldNameSplitter( fieldName ).isAParentNavigation() ) { + return setParentField( fieldName, value ); + } + return setDirectField( fieldName, value ); + } + + /** + * Set the specified parent relationship to the specified sfab_FabricatedSObject + * + * Is a more specific version of set( String fieldName, Object value ) + * Is a synonym of set( String relationshipName, sfab_FabricatedSObject fabricatedParent ) + * + * @param String - The relationship to set + * @param sfab_FabricatedSObject - The object that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject setParent( String relationshipName, sfab_FabricatedSObject fabricatedParent ) { + if ( new sfab_FieldNameSplitter( relationshipName ).isAParentNavigation() ) { + return setParentField( relationshipName, fabricatedParent ); + } + return setDirectParent( relationshipName, fabricatedParent ); + } + + /** + * Set the specified child relationship to the specified list of sfab_FabricatedSObject + * + * Is a more specific version of set( String fieldName, Object value ) + * Is a synonym of set( String relationshipName, List fabricatedChildren ) + * + * @param String - The relationship to set + * @param List - The objects that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject setChildren( String relationshipName, List fabricatedChildren ) { + if ( new sfab_FieldNameSplitter( relationshipName ).isAParentNavigation() ) { + return setParentField( relationshipName, fabricatedChildren ); + } + return setDirectChildren( relationshipName, fabricatedChildren ); + } + + /** + * Add the specified sfab_FabricatedSObject to the specified child relationship, setting it as an empty list to start + * if it does not already exist + * + * Is a synonym of add( String relationshipName, sfab_FabricatedSObject fabricatedChild ) + * + * @param String - The relationship to set + * @param sfab_FabricatedSObject - The object to add to the child relationship's list + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + public sfab_FabricatedSObject addChild( String relationshipName, sfab_FabricatedSObject fabricatedChild ) { + + if ( new sfab_FieldNameSplitter( relationshipName ).isAParentNavigation() ) { + addParentChild( relationshipName, fabricatedChild ); + } else { + childFieldIsChildRelationship( relationshipName ); + checkTypeIsValidForChildRelationship( relationshipName, fabricatedChild ); + if ( !nodeExists( relationshipName ) ) { + setNode( relationshipName, new sfab_ChildRelationshipNode( relationshipName ) ); + } + getChildRelationshipNode( relationshipName ).addChild( fabricatedChild ); + + ortoo_FabricatedSobjectRegister.registerParentOfRelationship( this, relationshipName, fabricatedChild ); + + } + return this; + } + + /** + * Builds the SObject that this sfab_FabricatedSObject represents + * + * @return SObject - The built SObject + */ + public SObject toSObject() { + return internalToSobject( false ); + } + + /** + * Builds the SObject that this sfab_FabricatedSObject represents, in a form that means it can be persisted + * + * @return SObject - The built SObject + */ + public SObject toPersistableSobject() + { + return internalToSobject( true ); + } + + private Sobject internalToSobject( Boolean persistable ) + { + SObject newObject = (SObject)JSON.deserialize( JSON.serialize( serialize( persistable ) ), sType ); + postBuildProcess( newObject ); + return newObject; + } + + /** + * An internal method that should not be called directly. + * Is public because of the requirement to call it from other outer classes within the library + */ + public void postBuildProcess( SObject objectToProcess ) { + for ( String nodeName : nodes.keySet() ) { + nodes.get( nodeName )?.postBuildProcess( objectToProcess ); + } + } + + /** + * Internal method that should not be called directly. + */ + public virtual Map serialize() { + return serialize( false ); + } + + /** + * Internal method that should not be called directly. + */ + public virtual Map serialize( Boolean persistable ) { + Map fields = new Map(); + for (sfab_FabricatedSObjectNode node : nodes.values()) { + fields.putAll(node.serialize( persistable )); + } + return fields; + } + + /** + * Returns a String representation of the name of the SObject Type that this sfab_FabricatedSObject represents + * + * @return String - The name of the SObject Type + */ + public String getSobjectName() { + return String.valueOf( sType ); + } + + /** + * Returns the SObjectType representation of the SObject Type that this sfab_FabricatedSObject represents + * + * @return String - The name of the SObject Type + */ + 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 default + * @return sfab_FieldNameSplitter + */ + private sfab_FieldNameSplitter defaultParentField( String fieldName ) { + + sfab_FieldNameSplitter fieldNameSplitter = new sfab_FieldNameSplitter( fieldName ); + String parentFieldName = fieldNameSplitter.getParentFieldName(); + + checkFieldIsParentRelationship( parentFieldName ); + + if ( ! nodeExists( parentFieldName ) ) { + try { + set( parentFieldName, objectDescriber.buildFabricatedObjectForRelationship( getSobjectName(), parentFieldName ) ); + } catch ( Exception e ) { + throw new ParentRelationshipObjectCannotBeAutoCreatedException( 'Could not auto-assign an object for the field ' + fieldName + ': ' + e.getMessage() ); + } + } + + return fieldNameSplitter; + } + + /** + * Set the specified field, which exists directly on this object, to the specified value. + * + * Is a more specific version of set( String fieldName, Object value ) + * + * @param String - The field to set + * @param Object - The value that the field should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + private sfab_FabricatedSObject setDirectField( String fieldName, Object value ) { + + Schema.SobjectField theField = objectDescriber.getField( getSObjectName(), fieldName ); + + if ( theField == null ) { + + if ( objectDescriber.objectHasParentRelationshipNamed( getSobjectName(), fieldName ) ) { + throw new FieldIsNotSimpleFieldException( 'The field '+ getSobjectName() +'.' + fieldName + ' cannot to set to a primitive, it is a parent relationship field' ); + } + + if ( objectDescriber.objectHasChildRelationshipNamed( getSObjectName(), fieldName ) ) { + throw new FieldIsNotSimpleFieldException( 'The field '+ getSobjectName() +'.' + fieldName + ' cannot to set to a primitive, it is a child relationship field' ); + } + + throw new FieldDoesNotExistException( 'The field ' + getSobjectName() + '.' + fieldName + ' does not exist' ); + } + + return setNode( fieldName, new sfab_FieldValuePairNode( theField, value ) ); + } + + /** + * Set the specified parent relationship, which is directly on the current object + * to the specified sfab_FabricatedSObject + * + * @param String - The relationship to set + * @param sfab_FabricatedSObject - The object that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + private sfab_FabricatedSObject setDirectParent( String relationshipName, sfab_FabricatedSObject fabricatedParent ) { + checkFieldIsParentRelationship( relationshipName ); + checkTypeIsValidForParentRelationship( relationshipName, fabricatedParent ); + ortoo_FabricatedSobjectRegister.registerChildOfRelationship( this, relationshipName, fabricatedParent ); + return setNode( relationshipName, new sfab_ParentRelationshipNode(relationshipName, fabricatedParent)); + } + + /** + * Set the specified child relationship, which is directly on the current object + * to the specified list of sfab_FabricatedSObject + * + * @param String - The relationship to set + * @param List - The objects that the relationship should be set to + * @return sfab_FabricatedSObject - This, allowing for a fluent interface + */ + private sfab_FabricatedSObject setDirectChildren( String relationshipName, List fabricatedChildren ) { + childFieldIsChildRelationship( relationshipName ); + checkTypeIsValidForChildRelationship( relationshipName, fabricatedChildren ); + + for ( sfab_FabricatedSObject thisChild : fabricatedChildren ) + { + ortoo_FabricatedSobjectRegister.registerParentOfRelationship( this, relationshipName, thisChild ); + } + return setNode( relationshipName, new sfab_ChildRelationshipNode( relationshipName, fabricatedChildren ) ); + } + + /** + * Internal method that should not be called directly. + * Ensures that the field on the parent record specified in the field name is set to the specified value + */ + private sfab_FabricatedSObject setParentField( String fieldName, Object value ) { + sfab_FieldNameSplitter fields = defaultParentField( fieldName ); + getParentRelationshipNode( fields.getParentFieldName() ).set( fields.getChildFieldName(), value ); + return this; + } + + /** + * Internal method that should not be called directly. + * Ensures that the child relationship on the parent record specified in the field name has the specified object added + */ + private sfab_FabricatedSObject addParentChild( String fieldName, sfab_FabricatedSObject fabricatedChild ) { + sfab_FieldNameSplitter fields = defaultParentField( fieldName ); + getParentRelationshipNode( fields.getParentFieldName() ).add( fields.getChildFieldName(), fabricatedChild ); + return this; + } + + /** + * Internal method that should not be called directly. + * Checks that the specied node exists and is set to a value + */ + private Boolean nodeExists( String nodeName ) { + return nodes.containsKey( nodeName ); + } + + /** + * Internal method that should not be called directly. + * Sets the specified node to the specified value + */ + private sfab_FabricatedSObject setNode( String nodeName, sfab_FabricatedSObjectNode node ) { + nodes.put( nodeName, node ); + return this; + } + + /** + * Internal method that should not be called directly. + * Gets the current value of the specified node, throwing an excepton if it is not set + */ + private sfab_FabricatedSObjectNode getNode( String nodeName ) { + if ( ! nodeExists( nodeName ) ) { + throw new NodeNotSetException( 'Attempt to get the value of node "' + nodeName + '" when it was not set' ); + } + return nodes.get( nodeName ); + } + + /** + * Internal method that should not be called directly. + * Gets value of the specified field node, throwing an excepton if it is not set to a sfab_FieldValuePairNode + */ + private sfab_FieldValuePairNode getFieldNode( String fieldName ) { + return ((sfab_FieldValuePairNode)getNode( fieldName )); + } + + /** + * Internal method that should not be called directly. + * Gets value of the specified parent relationship node, throwing an excepton if it is not set to a sfab_ParentRelationshipNode + */ + private sfab_ParentRelationshipNode getParentRelationshipNode( String relationshipName ) { + return ((sfab_ParentRelationshipNode)getNode( relationshipName )); + } + + /** + * Internal method that should not be called directly. + * Gets value of the specified parent relationship node, throwing an excepton if it is not set to a sfab_ChildRelationshipNode + */ + private sfab_ChildRelationshipNode getChildRelationshipNode( String relationshipName ) { + return ((sfab_ChildRelationshipNode)getNode( relationshipName )); + } + + /** + * Internal method that should not be called directly. + * Gets the field value of the specified field. + */ + @testVisible + private Object getFieldValue( String fieldName ) { + return getFieldNode( fieldName )?.getValue(); + } + + /** + * Internal method that should not be called directly. + * Gets the parent fabricated object of the specified parent relationship field + */ + @testVisible + private sfab_FabricatedSObject getParent( String relationshipName ) { + return getParentRelationshipNode( relationshipName )?.getParent(); + } + + /** + * Internal method that should not be called directly. + * Gets the child fabricated objects of the specified child relationship field + */ + @testVisible + private List getChildren( String relationshipName ) { + return getChildRelationshipNode( relationshipName )?.getChildren(); + } + + /** + * Internal method that should not be called directly. + * Gets the number of child fabricated objects held in the specified child relationship field + */ + @testVisible + private Integer getNumberOfChildren( String relationshipName ) { + return getChildRelationshipNode( relationshipName )?.getNumberOfChildren(); + } + + /** + * Internal method that should not be called directly. + * Gets the total number of nodes (being fields and relationships) that are set on this object + */ + @testVisible + private Integer getNumberOfNodes() { + return nodes.size(); + } + + /** + * Internal method that should not be called directly. + * Checks if the field with the given name is a parent relationship, throwing an exception if not + */ + private void checkFieldIsParentRelationship( String parentFieldName ) { + + if ( !objectDescriber.objectHasParentRelationshipNamed( getSobjectName(), parentFieldName ) ) { + if ( objectDescriber.objectHasSimpleFieldNamed( getSobjectName(), parentFieldName ) + || objectDescriber.objectHasChildRelationshipNamed( getSobjectName(), parentFieldName ) ) { + throw new FieldIsNotParentRelationshipException( 'The field ' + getSobjectName() + '.' + parentFieldName + ' is not a parent relationship' ); + } + + throw new ParentRelationshipDoesNotExistException( 'The parent relationship ' + getSobjectName() + '.' + parentFieldName + ' does not exist' ); + } + } + + /** + * Internal method that should not be called directly. + * Checks if the field with the given name is a parent relationship for an sobject of the same type as the given Fabricated SObject + */ + // TODO: push into sfab_ParentRelationshipNode? + private void checkTypeIsValidForParentRelationship( String relationshipName, sfab_FabricatedSObject fabricatedParent ){ + List validObjectTypes = objectDescriber.getObjectTypesForParentRelationship( getSobjectName(), relationshipName ); + if ( ! validObjectTypes.contains( fabricatedParent.getSobjectName() ) ) { + throw new FieldIsADifferentTypeException( 'The field ' + getSobjectName() + '.' + relationshipName + ' is ' + String.join( validObjectTypes, ',' ) + ', not ' + fabricatedParent.getSobjectName() ); + } + } + + /** + * Internal method that should not be called directly. + * Checks if the field with the given name is a child relationship for an sobject of the same type as the given Fabricated SObject + */ + // TODO: push into sfab_ChildRelationshipNode? + private void checkTypeIsValidForChildRelationship( String relationshipName, sfab_FabricatedSObject fabricatedChild ){ + String validObjectType = objectDescriber.getObjectTypeForChildRelationship( getSobjectName(), relationshipName ); + if ( validObjectType != fabricatedChild.getSobjectName() ) { + throw new FieldIsADifferentTypeException( 'The relationship ' + getSobjectName() + '.' + relationshipName + ' is ' + validObjectType + ', not ' + fabricatedChild.getSobjectName() ); + } + } + + /** + * Internal method that should not be called directly. + * Checks if the field with the given name is a child relationship for an sobject of the same type as the given Fabricated SObject + */ + // TODO: push into sfab_ChildRelationshipNode? + private void checkTypeIsValidForChildRelationship( String relationshipName, List fabricatedChildren ){ + for ( sfab_FabricatedSObject thisChild : fabricatedChildren ) { + checkTypeIsValidForChildRelationship( relationshipName, thisChild ); + } + } + + /** + * Internal method that should not be called directly. + * Checks if the fields with the given name is a child relationship, throwing an exception if not + */ + private void childFieldIsChildRelationship( String childRelationshipName ) { + + if ( !objectDescriber.objectHasChildRelationshipNamed( getSobjectName(), childRelationshipName ) ) { + if ( objectDescriber.objectHasSimpleFieldNamed( getSobjectName(), childRelationshipName ) + || objectDescriber.objectHasParentRelationshipNamed( getSobjectName(), childRelationshipName ) ) { + throw new FieldIsNotChildRelationshipException( 'The field ' + getSobjectName() + '.' + childRelationshipName + ' is not a child relationship' ); + } + + throw new ChildRelationshipDoesNotExistException( 'The child relationship ' + getSobjectName() + '.' + childRelationshipName + ' does not exist' ); + } + } + + /** + * Internal class that interprets a field name and determines + * if it represents a parent.child specification + */ + class sfab_FieldNameSplitter { + + private String fullFieldName; + + public sfab_FieldNameSplitter( String fullFieldName ) { + this.fullFieldName = fullFieldName; + } + + /** + * Internal method that should not be called directly. + * Returns the parent field component of the current field name + */ + public String getParentFieldName() { + return fullFieldName.substringBefore( '.' ); + } + + /** + * Internal method that should not be called directly. + * Returns the child field component of the current field name + */ + public String getChildFieldName() { + return fullFieldName.substringAfter( '.' ); + } + + /** + * Internal method that should not be called directly. + * States if the current field name represents a navigation to a parent field + */ + public Boolean isAParentNavigation() { + return fullFieldName.contains( '.' ); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // sfab_FabricatedSObjectNode implementations + // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public class sfab_FieldValuePairNode implements sfab_FabricatedSObjectNode { + + @testVisible protected Schema.SObjectField field; + @testVisible protected Object value; + + public sfab_FieldValuePairNode(Schema.SObjectField field, Object value) { + this.field = field; + this.value = value; + } + + public String getName() { + return field?.getDescribe().getName(); + } + + public Object getValue() { + return value; + } + + public Map serialize( Boolean persistable ) { + + Map noNodes = new Map(); + + if ( persistable && getName().equals( 'Id' ) ) { + System.debug( 'Id with the value "' + value + '" stripped from object' ); + return noNodes; // Ids cannot be set when persisting an object - everything is an insert + } + if ( fieldIsBlob() ) { + return noNodes; // is handled by the post process instead as deserializing a blob field does not work (API 50) + } else { + return new Map { getName() => value }; + } + } + + public void postBuildProcess( Object objectToProcess ) { + if ( fieldIsBlob() ) { + Blob valueToSet; + if ( value instanceOf Blob ) { + valueToSet = (Blob)value; + } else if ( value instanceOf String ) { + valueToSet = Blob.valueOf( (String)value ); + } + ((Sobject)objectToProcess).put( field, valueToSet ); + } + } + + private Boolean fieldIsBlob() { + return field.getDescribe().getType() == Schema.DisplayType.BASE64; + } + } + + public class sfab_ChildRelationshipNode implements sfab_FabricatedSObjectNode { + + @testVisible private String fieldName; + @testVisible private List children; + + public sfab_ChildRelationshipNode( String fieldName ) { + this.fieldName = fieldName; + this.children = new List(); + } + + public sfab_ChildRelationshipNode(String fieldName, List children) { + this.fieldName = fieldName; + this.children = children; + } + + public sfab_ChildRelationshipNode addChild( sfab_FabricatedSObject child ) { + this.children.add( child ); + return this; + } + + public String getName() { + return fieldName; + } + + public Integer getNumberOfChildren() { + return children.size(); + } + + public List getChildren() { + return children; + } + + public Map serialize( Boolean persistable ) { + List> serializedChildren = new List>(); + + for (sfab_FabricatedSObject child : children) { + serializedChildren.add(child.serialize( persistable )); + } + + return new Map { + fieldName => new Map { + 'totalSize' => children.size(), + 'done' => true, + 'records' => serializedChildren + } + }; + } + + public void postBuildProcess( Object objectToProcess ) { + // Note: this relies on the deserialization of the Fabricated SObject returning the + // child objects in same order as the input map, otherwise the Blob field values + // will be applied to the wrong objects. As far as we can tell, this is reliable + // at the time of writing. If it becomes not the case, this will need re-writing + if ( objectToProcess == null ) + { + return; // TODO: why is this needed - why is this sometimes null? + } + + List childSobjects = ((Sobject)objectToProcess).getSObjects( fieldName ); + for ( Integer i=0; i serialize( Boolean persistable ) { + + Object serializedParent = parent.serialize( persistable ); + Map fields = new Map(); + if ( ! persistable ) + { + fields.put( fieldName, serializedParent ); + } + return fields; + } + + public void postBuildProcess( Object objectToProcess ) { + parent.postBuildProcess( ((Sobject)objectToProcess).getSobject( fieldName ) ); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Stub implementations + // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public class sfab_FabricatedSObjectStub extends sfab_FabricatedSObject { + private Map serializedMap; + @testVisible private Boolean serializeInvoked = false; + @testVisible private Boolean serializeInvokedWith = null; + + public sfab_FabricatedSObjectStub(Type sType) { + super(sType); + this.serializedMap = new Map(); + } + + public sfab_FabricatedSObjectStub(Type sType, Map serializedMap) { + super(sType); + this.serializedMap = serializedMap; + } + + public override Map serialize( Boolean persistable ) { + serializeInvoked = true; + serializeInvokedWith = persistable; + return serializedMap; + } + } + + public class sfab_FabricatedSObjectNodeStub implements sfab_FabricatedSObjectNode { + public Boolean serializeInvoked = false; + public Map serializedNode; + private String name; + + public sfab_FabricatedSObjectNodeStub( String name ) { + serializedNode = new Map(); + this.name = name; + } + + public String getName() { + return name; + } + + public sfab_FabricatedSObjectNodeStub(Map serializedNode) { + this.serializedNode = serializedNode; + } + + public Map serialize( Boolean persistable ) { + serializeInvoked = true; + return serializedNode; + } + + public void postBuildProcess( Object objectToProcess ) { + } + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls-meta.xml b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/sfab_FabricatedSObject.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls b/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls new file mode 100644 index 00000000000..a93395fdd22 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls @@ -0,0 +1,392 @@ +/** + * Part of the SObject Fabricator library, providing the ability to request information about fields and relationships + * that exist against particular objects. + * + * Is instantiated and then questions asked of it, caching the describers for given objects as they are requested. + */ +public with sharing class sfab_ObjectDescriber { + + public class ParentRelationshipObjectCannotBeAutoCreatedException extends Exception {} + public class ParentRelationshipDoesNotExistException extends Exception {} + + static Map singleObjectDescribers = new Map(); + + /** + * Retrieve the SobjectField for the given object + field name combination. + * + * @param String The name of the object on which the requested field resides + * @param String The name of the field to retrieve + * @return SobjectField The requested field + */ + public SobjectField getField( String objectName, String fieldName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.getField( fieldName ); + } + + /** + * Retrieve the SobjectField that is used for defining the object Id for the specified child relationship + * + * @param String The name of the parent object + * @param String The name of the relationship to retrieve the field for + * @return SobjectField The requested field + */ + public SObjectField getFieldForChildRelationship( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.getChildRelationshipNamed( relationshipName )?.getField(); + } + + /** + * Retrieve the SobjectField that is used for defining the object Id for the specified parent relationship + * + * @param String The name of the child object + * @param String The name of the relationship to retrieve the field for + * @return SobjectField The requested field + */ + public SObjectField getFieldForParentRelationship( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.getParentRelationshipNamed( relationshipName )?.getField(); + } + + /** + * States if the specified object has a 'simple' field (not a relationship) with the given name + * + * @param String The name of the object on which the requested field potentially resides + * @param String The name of the field to check + * @return Boolean States if the field exists and is 'simple' + */ + public Boolean objectHasSimpleFieldNamed( String objectName, String fieldName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.hasSimpleFieldNamed( fieldName ); + } + + /** + * States if the specified object has a parent relationship with the given name + * + * @param String The name of the object on which the requested relationship potentially resides + * @param String The name of the relationship to check + * @return Boolean States if the relationship exists + */ + public Boolean objectHasParentRelationshipNamed( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.hasParentRelationshipNamed( relationshipName ); + } + + /** + * Retrieves a list of names of objects that the given parent relationship allows + * + * @param String The name of the object on which the requested relationship potentially resides + * @param String The name of the relationship to check + * @return List The names of the objects that the given relationship supports + */ + public List getObjectTypesForParentRelationship( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.getValidObjectsForParentRelationship( relationshipName ); + } + + /** + * States if the specified object has a child relationship with the given name + * + * @param String The name of the object on which the requested relationship potentially resides + * @param String The name of the relationship to check + * @return Boolean States if the relationship exists + */ + public Boolean objectHasChildRelationshipNamed( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.hasChildRelationshipNamed( relationshipName ); + } + + /** + * Retrieves a list of names of objects that the given child relationship allows + * + * @param String The name of the object on which the requested relationship potentially resides + * @param String The name of the relationship to check + * @return List The names of the objects that the given relationship supports + */ + public String getObjectTypeForChildRelationship( String objectName, String relationshipName ) { + ensureSingleObjectDescriberIsInitialised( objectName ); + return getObjectDescriber( objectName )?.getValidObjectForChildRelationship( relationshipName ); + } + + /** + * Builds an sfab_FabricatedSObject for the given relationship, if the relationship only allows a single object type. + * + * If the relationship is polymorphic (or does not exist), then an exception is thrown. + * + * @param String The name of the object on which the requested relationship potentially resides + * @param String The name of the relationship to contsruct the object for + * @return sfab_FabricatedSObject The constructed object for this relationship + */ + // TODO: should this actually be in FabricatedObject? At this point we are no longer describing the object! + public sfab_FabricatedSObject buildFabricatedObjectForRelationship( String objectName, String relationshipName ) { + + if ( String.isEmpty( objectName ) ) { + throw new ParentRelationshipDoesNotExistException( 'Cannot automatically create a Fabricated Object for the relationship because the object name has not been specified' ); + } + + if ( String.isEmpty( relationshipName ) ) { + throw new ParentRelationshipDoesNotExistException( 'Cannot automatically create a Fabricated Object for the relationship because the relationship name has not been specified' ); + } + + ensureSingleObjectDescriberIsInitialised( objectName ); + SingleObjectDescriber relationshipDescriber = getObjectDescriber( objectName ); + + if ( ! relationshipDescriber.objectExists() ) { + throw new ParentRelationshipDoesNotExistException( 'Cannot automatically create a Fabricated Object for the relationship ' + objectName + '.' + relationshipName + ' because the object type does not exist' ); + } + if ( ! relationshipDescriber.hasParentRelationshipNamed( relationshipName ) ) { + throw new ParentRelationshipDoesNotExistException( 'Cannot automatically create a Fabricated Object for the relationship ' + objectName + '.' + relationshipName + ' because the relationship does not exist' ); + } + + return relationshipDescriber.buildFabricatedObjectForRelationship( relationshipName ); + } + + private void ensureSingleObjectDescriberIsInitialised( String objectName ) { + if ( ! singleObjectDescribers.containsKey( objectName ) ) { + singleObjectDescribers.put( objectName, new SingleObjectDescriber( objectName ) ); + } + } + + private SingleObjectDescriber getObjectDescriber( String objectName ) { + return singleObjectDescribers.get( objectName ); + } + + private class SingleObjectDescriber { + + private String objectName; + + Boolean fieldsInitialised = false; + + private Schema.DescribeSObjectResult objectDescribe { + get { + if ( objectDescribe == null ) { + objectDescribe = Schema.getGlobalDescribe() + ?.get( objectName ) + ?.getDescribe(); + } + return objectDescribe; + } + set; + } + + private Map fields { + get { + if ( ! fieldsInitialised ) { + fieldsInitialised = true; + fields = objectDescribe + ?.fields + ?.getMap(); + } + return fields; + } + set; + } + + private Map parentRelationships { + get { + if ( parentRelationships == null ) { + parentRelationships = new Map(); + + if ( fields != null ) { + for ( SObjectField thisField : fields.values() ) { + ParentRelationship relationship = new ParentRelationship() + .setChildObjectName( objectName ) + .setFieldDescribe( thisField.getDescribe() ); + if ( relationship.isValid() ) { + parentRelationships.put( relationship.getName(), relationship ); + } + } + } + } + return parentRelationships; + } + set; + } + + private Map childRelationships { + get { + if ( childRelationships == null ) { + + List schemaRelationships = objectDescribe?.getChildRelationships(); + if ( schemaRelationships == null ) { + return new Map(); + } + + childRelationships = new Map(); + + for( Schema.ChildRelationship thisSchemaChildRelationship : schemaRelationships ) { + + ChildRelationship relationship = new ChildRelationship() + .setParentObjectName( objectName ) + .setSchemaRelationship( thisSchemaChildRelationship ); + + if ( relationship.isValid() ) { + childRelationships.put( relationship.getName(), relationship ); + } + } + } + return childRelationships; + } + set; + } + + public SingleObjectDescriber( String objectName ) { + this.objectName = objectName; + } + + public Boolean objectExists() { + return fields != null; + } + + public Boolean hasFieldNamed( String fieldName ) { + return objectExists() && fields.containsKey( fieldName ); + } + + public SObjectField getField( String fieldName ) { + if ( ! hasFieldNamed( fieldName ) ) { + return null; + } + return fields.get( fieldName ); + } + + public Boolean hasSimpleFieldNamed( String fieldName ) { + return objectExists() && hasFieldNamed( fieldName ) && ! hasParentRelationshipNamed( fieldName ) && ! hasChildRelationshipNamed( fieldName ); + } + + public Boolean hasParentRelationshipNamed( String relationshipName ) { + return objectExists() && parentRelationships.containsKey( relationshipName ); + } + + public Boolean hasChildRelationshipNamed( String relationshipName ) { + return objectExists() && childRelationships.containsKey( relationshipName ); + } + + public List getValidObjectsForParentRelationship( String relationshipName ) { + if ( !hasParentRelationshipNamed( relationshipName ) ) { + return new List(); + } + List validObjects = getParentRelationshipNamed( relationshipName ).getTargetObjectTypes(); + validObjects.sort(); + return validObjects; + } + + public String getValidObjectForChildRelationship( String relationshipName ) { + if ( !hasChildRelationshipNamed( relationshipName ) ) { + return null; + } + return getChildRelationshipNamed( relationshipName )?.getChildObjectType(); + } + + public sfab_FabricatedSObject buildFabricatedObjectForRelationship( String relationshipName ) { + return getParentRelationshipNamed( relationshipName )?.buildFabricatedObject(); + } + + private ParentRelationship getParentRelationshipNamed( String relationshipName ) { + return parentRelationships.get( relationshipName ); + } + + private ChildRelationship getChildRelationshipNamed( String relationshipName ) { + return childRelationships.get( relationshipName ); + } + } + + private class ParentRelationship { + + private String childObjectName; + private Schema.DescribeFieldResult fieldDescribe; + + public ParentRelationship setFieldDescribe( Schema.DescribeFieldResult fieldDescribe ) { + this.fieldDescribe = fieldDescribe; + return this; + } + + public ParentRelationship setChildObjectName( String childObjectName ) { + this.childObjectName = childObjectName; + return this; + } + + public String getName() { + return fieldDescribe.getRelationshipName(); + } + + public String getFullName() { + return childObjectName + '.' + getName(); + } + + public String getFieldName() { + return fieldDescribe.getName(); + } + + public SobjectField getField() { + return fieldDescribe.getSobjectField(); + } + + public List getTargetObjectTypes() { + + if ( !isValid() ) { + return new List(); + } + + List targetObjectTypes = new List(); + for ( Schema.sObjectType thisReferenceTo : fieldDescribe.getReferenceTo() ) { + targetObjectTypes.add( thisReferenceTo.getDescribe().getName() ); + } + return targetObjectTypes; + } + + public sfab_FabricatedSObject buildFabricatedObject() { + if ( isPolymorphic() ) { + throw new ParentRelationshipObjectCannotBeAutoCreatedException( 'Cannot automatically create a Fabricated Object for the relationship ' + getFullName() + ' as it is polymorphic and so not possible to automatically ascertain which SObject to use' ); + } + return new sfab_FabricatedSObject( Type.forName( getTargetObjectTypes()[0] ) ); + } + + public Boolean isValid() { + return fieldDescribe?.getReferenceTo()?.size() > 0; + } + + private Boolean isPolymorphic() { + return fieldDescribe.getReferenceTo().size() > 1; + } + } + + private class ChildRelationship { + + private Schema.ChildRelationship schemaRelationship; + private String parentObjectName; + + public ChildRelationship setSchemaRelationship( Schema.ChildRelationship schemaRelationship ) { + this.schemaRelationship = schemaRelationship; + return this; + } + + public ChildRelationship setParentObjectName( String parentObjectName ) { + this.parentObjectName = parentObjectName; + return this; + } + + public String getParentObjectName() { + return parentObjectName; + } + + public String getFullName() { + return parentObjectName + '.' + getName(); + } + + public String getName() { + return schemaRelationship.getRelationshipName(); + } + + public String getChildObjectType() { + return String.valueOf( schemaRelationship.getChildSObject() ); + } + + public SobjectField getField() { + return schemaRelationship.getField(); + } + // Some don't have names - and if they don't have names they can't possibly be + // set by the fabricator, so must be invalid. + public Boolean isValid() { + return getName() != null; + } + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls-meta.xml b/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/sfab_ObjectDescriber.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls b/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls new file mode 100644 index 00000000000..4d11b73ba49 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls @@ -0,0 +1,250 @@ +@isTest +private class ortoo_FabricatedSObjectRegisterTest { + + @isTest + private static void registerObject_whenCalled_willQueueTheObjectForCommit() // NOPMD: Test method name format + { + Amoss_Instance uowController = ApplicationMockRegistrar.registerMockUnitOfWork(); + + Amoss_Instance fabricatedContactController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedContact = (sfab_FabricatedSobject)fabricatedContactController.generateDouble(); + + Contact contactToPersist = new Contact( FirstName = 'firstName' ); + + fabricatedContactController + .expects( 'getSobjectType' ) + .returning( Contact.sobjectType ) + .then() + .expects( 'toPersistableSobject' ) + .returning( contactToPersist ); + + uowController + .expects( 'registerNew' ) + .withParameter( contactToPersist ) + .then() + .expects( 'commitWork' ); + + Test.startTest(); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContact ); + ortoo_FabricatedSObjectRegister.persist(); + Test.stopTest(); + + fabricatedContactController.verify(); + uowController.verify(); + } + + @isTest + private static void registerObject_whenCalledMultipleTimes_willQueueEachObjectForCommit() // NOPMD: Test method name format + { + Amoss_Instance uowController = ApplicationMockRegistrar.registerMockUnitOfWork(); + + Amoss_Instance fabricatedContactController = new Amoss_Instance( sfab_FabricatedSobject.class ); + List fabricatedContacts = new List + { + (sfab_FabricatedSobject)fabricatedContactController.generateDouble(), + (sfab_FabricatedSobject)fabricatedContactController.generateDouble(), + (sfab_FabricatedSobject)fabricatedContactController.generateDouble() + }; + + List contactsToPersist = new List + { + new Contact( FirstName = 'firstName' ), + new Contact( FirstName = 'firstName2' ), + new Contact( FirstName = 'firstName3' ) + }; + + fabricatedContactController + .when( 'getSobjectType' ) + .returns( Contact.sobjectType ) + .also() + .expects( 'toPersistableSobject' ) + .returning( contactsToPersist[0] ) + .then() + .expects( 'toPersistableSobject' ) + .returning( contactsToPersist[1] ) + .then() + .expects( 'toPersistableSobject' ) + .returning( contactsToPersist[2] ); + + uowController + .expects( 'registerNew' ) + .withParameter( contactsToPersist[0] ) + .then() + .expects( 'registerNew' ) + .withParameter( contactsToPersist[1] ) + .then() + .expects( 'registerNew' ) + .withParameter( contactsToPersist[2] ) + .then() + .expects( 'commitWork' ); + + Test.startTest(); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContacts[0] ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContacts[1] ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContacts[2] ); + ortoo_FabricatedSObjectRegister.persist(); + Test.stopTest(); + + fabricatedContactController.verify(); + uowController.verify(); + } + + @isTest + private static void registerChildOfRelationship_whenCalled_willSetupARelationship() // NOPMD: Test method name format + { + Amoss_Instance uowController = ApplicationMockRegistrar.registerMockUnitOfWork(); + + Amoss_Instance fabricatedContactController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedContact = (sfab_FabricatedSobject)fabricatedContactController.generateDouble(); + + Amoss_Instance fabricatedAccountController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedAccount = (sfab_FabricatedSobject)fabricatedAccountController.generateDouble(); + + Contact contactToPersist = new Contact( FirstName = 'firstName' ); + Account accountToPersist = new Account( Name = 'accountName' ); + + configureToReturn( fabricatedContactController, contactToPersist ); + configureToReturn( fabricatedAccountController, accountToPersist ); + + uowController + .expects( 'registerNew' ) + .withParameter( contactToPersist ) + .then() + .expects( 'registerNew' ) + .withParameter( accountToPersist ) + .then() + .expects( 'registerRelationship' ) + .withParameter( contactToPersist ) + .thenParameter( Contact.AccountId ) + .thenParameter( accountToPersist ) + .then() + .expects( 'commitWork' ); + + Test.startTest(); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContact ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedAccount ); + ortoo_FabricatedSObjectRegister.registerChildOfRelationship( fabricatedContact, 'Account', fabricatedAccount ); + ortoo_FabricatedSObjectRegister.persist(); + Test.stopTest(); + + uowController.verify(); + } + + @isTest + private static void registerParentOfRelationship_whenCalled_willSetupARelationship() // NOPMD: Test method name format + { + Amoss_Instance uowController = ApplicationMockRegistrar.registerMockUnitOfWork(); + + Amoss_Instance fabricatedContactController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedContact = (sfab_FabricatedSobject)fabricatedContactController.generateDouble(); + + Amoss_Instance fabricatedAccountController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedAccount = (sfab_FabricatedSobject)fabricatedAccountController.generateDouble(); + + Contact contactToPersist = new Contact( FirstName = 'firstName' ); + Account accountToPersist = new Account( Name = 'accountName' ); + + configureToReturn( fabricatedContactController, contactToPersist ); + configureToReturn( fabricatedAccountController, accountToPersist ); + + uowController + .expects( 'registerNew' ) + .withParameter( contactToPersist ) + .then() + .expects( 'registerNew' ) + .withParameter( accountToPersist ) + .then() + .expects( 'registerRelationship' ) + .withParameter( contactToPersist ) + .thenParameter( Contact.AccountId ) + .thenParameter( accountToPersist ) + .then() + .expects( 'commitWork' ); + + Test.startTest(); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContact ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedAccount ); + ortoo_FabricatedSObjectRegister.registerParentOfRelationship( fabricatedAccount, 'Contacts', fabricatedContact ); + ortoo_FabricatedSObjectRegister.persist(); + Test.stopTest(); + + uowController.verify(); + } + + @isTest + private static void getOrderOfInserts_whenCalled_willWorkOutTheOrderForTheUow() // NOPMD: Test method name format + { + Amoss_Instance fabricatedOpportunityController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedOpportunity = (sfab_FabricatedSobject)fabricatedOpportunityController.generateDouble(); + + Amoss_Instance fabricatedContactController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedContact = (sfab_FabricatedSobject)fabricatedContactController.generateDouble(); + + Amoss_Instance fabricatedAccountController = new Amoss_Instance( sfab_FabricatedSobject.class ); + sfab_FabricatedSobject fabricatedAccount = (sfab_FabricatedSobject)fabricatedAccountController.generateDouble(); + + Opportunity opportunityToPersist = new Opportunity( Name = 'opportunityName' ); + Contact contactToPersist = new Contact( FirstName = 'firstName' ); + Account accountToPersist = new Account( Name = 'accountName' ); + + configureToReturn( fabricatedOpportunityController, opportunityToPersist ); + configureToReturn( fabricatedContactController, contactToPersist ); + configureToReturn( fabricatedAccountController, accountToPersist ); + + Test.startTest(); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedContact ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedAccount ); + ortoo_FabricatedSObjectRegister.registerObject( fabricatedOpportunity ); + ortoo_FabricatedSObjectRegister.registerParentOfRelationship( fabricatedAccount, 'Contacts', fabricatedContact ); + ortoo_FabricatedSObjectRegister.registerParentOfRelationship( fabricatedAccount, 'Opportunities', fabricatedOpportunity ); + + List actualOrderOfInserts = ortoo_FabricatedSObjectRegister.instance.getOrderOfInserts(); + Test.stopTest(); + + List expectedOrderOfInserts = new List{ Account.sobjectType, Opportunity.sobjectType, Contact.sobjectType }; + + System.assertEquals( expectedOrderOfInserts, actualOrderOfInserts, 'getOrderOfInserts, when called, will work out the order for the Unit of Work' ); + } + + private static Amoss_Instance configureToReturn( Amoss_Instance controller, Contact contactToReturn ) + { + controller + .when( 'getSobjectName' ) + .returns( 'Contact' ) + .also() + .when( 'getSobjectType' ) + .returns( Contact.sobjectType ) + .also() + .when( 'toPersistableSobject' ) + .returns( contactToReturn ); + return controller; + } + + private static Amoss_Instance configureToReturn( Amoss_Instance controller, Account accountToReturn ) + { + controller + .when( 'getSobjectName' ) + .returns( 'Account' ) + .also() + .when( 'getSobjectType' ) + .returns( Account.sobjectType ) + .also() + .when( 'toPersistableSobject' ) + .returns( accountToReturn ); + return controller; + } + + private static Amoss_Instance configureToReturn( Amoss_Instance controller, Opportunity opportunityToReturn ) + { + controller + .when( 'getSobjectName' ) + .returns( 'Opportunity' ) + .also() + .when( 'getSobjectType' ) + .returns( Opportunity.sobjectType ) + .also() + .when( 'toPersistableSobject' ) + .returns( opportunityToReturn ); + return controller; + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls-meta.xml b/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/ortoo_FabricatedSObjectRegisterTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls new file mode 100644 index 00000000000..5dbcd1b4166 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls @@ -0,0 +1,172 @@ +@isTest +private class sfab_FabricatedSObjectPersistTest { + + @isTest + private static void persist_whenMultipleLevelsOfObjectsParentToChild_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Account.class ) + .set( Account.Name, 'New Account' ) + .add( 'Contacts', new sfab_FabricatedSobject( Contact.class ) + .set( Contact.LastName, 'last' ) + .add( 'Notes', new sfab_FabricatedSobject( Note.class ) + .set( Note.Title, 'the title 1' ) + .set( Note.Body, 'the body' ) + ) + ) + .add( 'Contacts', new sfab_FabricatedSobject( Contact.class ) + .set( Contact.LastName, 'last2' ) + .add( 'Notes', new sfab_FabricatedSobject( Note.class ) + .set( Note.Title, 'the title 2' ) + .set( Note.Body, 'the body' ) + ) + ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List accounts = getAccounts(); + System.assertEquals( 1, accounts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated - checking accounts' ); + System.assertEquals( 'New Account', accounts[0].Name, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking account field' ); + + List contacts = getContacts(); + System.assertEquals( 2, contacts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated - checking contacts' ); + + System.assertEquals( accounts[0].Id, contacts[0].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking contacts relationship field 1' ); + System.assertEquals( 'last', contacts[0].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking contacts field 1' ); + + System.assertEquals( accounts[0].Id, contacts[1].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking contacts relationship field 2' ); + System.assertEquals( 'last2', contacts[1].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking contacts field 2' ); + + List notes = getNotes(); + System.assertEquals( 2, notes.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated - checking notes' ); + + System.assertEquals( contacts[0].Id, notes[0].ParentId, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking notes relationship field 1' ); + System.assertEquals( 'the title 1', notes[0].title, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking notes field 1' ); + + System.assertEquals( contacts[1].Id, notes[1].ParentId, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking notes relationship field 2' ); + System.assertEquals( 'the title 2', notes[1].title, 'persist, when called, will create the fabricated sobjects that have been instantiated - checking notes field 2' ); + } + + @isTest + private static void persist_whenMultipleLevelsOfObjectsChildToParent_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Contact.class ) + .set( 'Account', new sfab_FabricatedSobject( Account.class ) + .set( Account.Name, 'New Account' ) + ) + .set( Contact.LastName, 'last' ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List accounts = getAccounts(); + System.assertEquals( 1, accounts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated child to parent - checking accounts' ); + System.assertEquals( 'New Account', accounts[0].Name, 'persist, when called, will create the fabricated sobjects that have been instantiated child to parent - checking account field' ); + + List contacts = getContacts(); + System.assertEquals( 1, contacts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated child to parent - checking contacts' ); + System.assertEquals( accounts[0].Id, contacts[0].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated child to parent - checking contacts relationship field 1' ); + System.assertEquals( 'last', contacts[0].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated child to parent - checking contacts field 1' ); + } + + @isTest + private static void persist_whenMultipleLevelsOfObjectsImpliedParent_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Contact.class ) + .set( 'Account.Name', 'New Account' ) + .set( Contact.LastName, 'last' ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List accounts = getAccounts(); + System.assertEquals( 1, accounts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent - checking accounts' ); + System.assertEquals( 'New Account', accounts[0].Name, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent - checking account field' ); + + List contacts = getContacts(); + System.assertEquals( 1, contacts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent - checking contacts' ); + System.assertEquals( accounts[0].Id, contacts[0].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent - checking contacts relationship field 1' ); + System.assertEquals( 'last', contacts[0].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent - checking contacts field 1' ); + } + + @isTest + private static void persist_whenMultipleLevelsOfObjectsImpliedParentWithChild_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Contact.class ) + .set( 'Account.Name', 'New Account' ) + .set( Contact.LastName, 'last' ) + .add( 'Account.Contacts', new sfab_FabricatedSobject( Contact.class ) + .set( Contact.LastName, 'last implied parent child' ) + ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List accounts = getAccounts(); + System.assertEquals( 1, accounts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking accounts' ); + System.assertEquals( 'New Account', accounts[0].Name, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking account field' ); + + List contacts = getContacts(); + System.assertEquals( 2, contacts.size(), 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking contacts' ); + + System.assertEquals( accounts[0].Id, contacts[0].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking contacts relationship field 1' ); + System.assertEquals( 'last', contacts[0].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking contacts field 1' ); + + System.assertEquals( accounts[0].Id, contacts[1].AccountId, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking contacts relationship field 2' ); + System.assertEquals( 'last implied parent child', contacts[1].LastName, 'persist, when called, will create the fabricated sobjects that have been instantiated with an implied parent with child - checking contacts field 2' ); + } + + @isTest + private static void persist_whenAnUnsettableFieldIsSet_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Contact.class ) + .set( Contact.LastName, 'last' ) + .set( Contact.Name, 'unsettable' ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List contacts = getContacts(); + System.assertEquals( 1, contacts.size(), 'persist, when an unsettable field has been set, will still save - checking contacts' ); + + System.assertEquals( 'last', contacts[0].LastName, 'persist, when an unsettable field has been set, will still save - checking contacts field 1' ); + System.assertEquals( 'last', contacts[0].Name, 'persist, when an unsettable field has been set, will still save - checking contacts field 2' ); + } + @isTest + private static void persist_whenIdIsSet_willSave() // NOPMD: Test method name format + { + new sfab_FabricatedSobject( Contact.class ) + .set( Contact.LastName, 'last' ) + .set( Contact.Id, 'a fake Id' ); + + Test.startTest(); + ortoo_FabricatedSobjectRegister.persist(); + Test.stopTest(); + + List contacts = getContacts(); + System.assertEquals( 1, contacts.size(), 'persist, when an Id has been set, will still save, but without the specified Id - checking contacts' ); + + System.assertEquals( 'last', contacts[0].LastName, 'persist, when an Id has been set, will still save, but without the specified Id - checking contacts field 1' ); + System.assertNotEquals( 'a fake Id', contacts[0].Id, 'persist, when an Id has been set, will still save, but without the specified Id - checking contacts Id field' ); + } + + private static List getAccounts() + { + return [SELECT Name FROM Account ORDER BY Id]; + } + + private static List getContacts() + { + return [SELECT AccountId, LastName, Name FROM Contact ORDER BY LastName]; + } + + private static List getNotes() + { + return[ SELECT ParentId, Title FROM Note ORDER BY Title]; + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls-meta.xml b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectPersistTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls new file mode 100644 index 00000000000..2cfec6f5dc6 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls @@ -0,0 +1,1181 @@ +@isTest +private class sfab_FabricatedSObjectTest { + @isTest + private static void constructor_expectFieldsSetViaSObjectField() { + Map accountFields = new Map { + Account.Id => 'Id-1', + Account.Name => 'Foo' + }; + + sfab_FabricatedSObject fabricatedAccount = new sfab_FabricatedSObject(Account.class, accountFields); + System.assertEquals(accountFields.size(), fabricatedAccount.getNumberOfNodes()); + } + + @isTest + private static void constructor_expectFieldsSetViaString() { + Map accountFields = new Map { + 'Id' => 'Id-1', + 'Name' => 'Foo' + }; + + sfab_FabricatedSObject fabricatedAccount = new sfab_FabricatedSObject(Account.class, accountFields); + System.assertEquals(accountFields.size(), fabricatedAccount.getNumberOfNodes()); + } + + @isTest + private static void toSObject_expectSpecifiedSObjectType() { + SObject sObj = new sfab_FabricatedSObject(Account.class).toSObject(); + System.assert(sObj instanceof Account); + } + + @isTest + private static void toSObject_expectSerializeInvokedOnNodes() { + sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub node1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub( 'node1' ); + sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub node2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub( 'node2' ); + SObject sObj = new sfab_FabricatedSObject(Account.class, new List { node1, node2 }).toSObject(); + System.assert(node1.serializeInvoked); + System.assert(node2.serializeInvoked); + } + + @isTest + private static void toSObject_expectProperties() { + Map fields = new Map { 'Id' => 'id-1', 'Name' => 'Foo' }; + sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub node = new sfab_FabricatedSObject.sfab_FabricatedSObjectNodeStub(fields); + SObject sObj = new sfab_FabricatedSObject(Account.class, new List { node }).toSObject(); + System.assertEquals(fields.get('Id'), sObj.Id); + System.assertEquals(fields.get('Name'), sObj.get('Name')); + } + + @isTest + private static void setField_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.setField(Account.Id, 'Id-1'); + System.assertEquals(1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + } + + @isTest + private static void setField_whenString_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.setField('Id', 'Id-1'); + System.assertEquals(1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + } + + @isTest + private static void setField_whenStringIsAParentField_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + fabricatedSObject.setField( 'Account.Id', 'Id-1' ); + System.assertEquals( 1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 'Id-1', fabricatedSObject.getParent( 'Account' ).getFieldValue( 'Id' ) ); + } + + @isTest + private static void set_whenField_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.set(Account.Id, 'Id-1'); + System.assertEquals(1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + } + + @isTest + private static void set_whenStringField_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.set('Id', 'Id-1'); + System.assertEquals(1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + } + + @isTest + private static void set_whenStringFieldForParent_expectParentAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Contact.class); + fabricatedSObject.set('Account.Id', 'Account-Id-1'); + fabricatedSObject.set('Account.Name', 'Account-Name'); + fabricatedSObject.set('Id', 'Contact-Id-1'); + System.assertEquals(2, fabricatedSObject.getNumberOfNodes()); + + sfab_FabricatedSObject fabricatedAccount = fabricatedSObject.getParent( 'Account' ); + System.assertNotEquals(null, fabricatedAccount); + + System.assertEquals( 'Account-Id-1', fabricatedAccount.getFieldValue( 'Id' ) ); + System.assertEquals( 'Account-Name', fabricatedAccount.getFieldValue( 'Name' ) ); + System.assertEquals( 'Contact-Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + } + + + @isTest + private static void set_whenMapSobjectField_expectFieldsAddedToNodes() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + + Map fields = new Map { + Account.Id => 'Id-1', + Account.Name => 'Foo' + }; + + fabricatedSObject.set(fields); + System.assertEquals( fields.size(), fabricatedSObject.getNumberOfNodes()); + + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ) ); + System.assertEquals( 'Foo', fabricatedSObject.getFieldValue( 'Name' ) ); + } + + @isTest + private static void set_whenMapString_expectFieldsParentsAndChildrenAddedToNodes() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + + Map fields = new Map { + 'Id' => 'Id-1', + 'Name' => 'Foo', + 'Contacts' => new List{ + new sfab_FabricatedSObject( Contact.class ) + .set( 'Name', 'ContactName' ) + }, + 'Owner' => new sfab_FabricatedSObject( User.class ) + .set( 'Username', 'The user' ) + }; + + fabricatedSObject.set(fields); + System.assertEquals( 4, fabricatedSObject.getNumberOfNodes(), 'set, when given a map indexed by string, will add each of the fields' ); + + System.assertEquals( 'Id-1', fabricatedSObject.getFieldValue( 'Id' ), 'set, when given a map indexed by string, will add each of the fields, setting the value of a simple field' ); + System.assertEquals( 'Foo', fabricatedSObject.getFieldValue( 'Name' ), 'set, when given a map indexed by string, will add each of the fields, setting the value of a simple field' ); + System.assertEquals( 1, fabricatedSObject.getNumberOfChildren( 'Contacts' ), 'set, when given a map indexed by string, will add each of the fields, setting the value of a child relationship field' ); + System.assertEquals( 1, fabricatedSObject.getParent( 'Owner' ).getNumberOfNodes(), 'set, when given a map indexed by string, will add each of the fields, setting the value of a parent relationship field' ); + } + + @isTest + private static void set_whenMapString_expectMultiLevelParentFieldsAddedToNodes() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + + Map fields = new Map { + 'Owner.Contact' => new sfab_FabricatedSObject( Contact.class ) + .set( 'LastName', 'TheOwnerLast' ), + 'Owner.Contact.FirstName' => 'TheOwnerFirst' + }; + + fabricatedSObject.set(fields); + + System.assertEquals( 1, fabricatedSObject.getNumberOfNodes() ); + + sfab_FabricatedSObject fabricatedOwner = fabricatedSObject.getParent( 'Owner' ); + System.assertEquals( 1, fabricatedOwner.getNumberOfNodes() ); + + sfab_FabricatedSObject fabricatedContact = fabricatedOwner.getParent( 'Contact' ); + System.assertEquals( 'TheOwnerLast', fabricatedContact.getFieldValue( 'LastName' ) ); + System.assertEquals( 'TheOwnerFirst', fabricatedContact.getFieldValue( 'FirstName' ) ); + } + + @isTest + private static void set_whenMapString_expectMultiLevelChildFieldsAddedToNodes() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Contact.class); + + Map fields = new Map { + 'Account.Opportunities' => new List{ + new sfab_FabricatedSObject( Opportunity.class ) + .set( 'Name', 'The Opportunity' ) + } + }; + + fabricatedSObject.set(fields); + + System.assertEquals( 1, fabricatedSObject.getNumberOfNodes() ); + + sfab_FabricatedSObject fabricatedAccount = fabricatedSObject.getParent( 'Account' ); + List fabricatedOportunities = fabricatedAccount.getChildren( 'Opportunities' ); + + System.assertEquals( 1, fabricatedOportunities.size() ); + System.assertEquals( 'The Opportunity', fabricatedOportunities[0].getFieldValue( 'Name' ) ); + } + + @isTest + private static void setParent_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Opportunity.class); + fabricatedSObject.setParent('Account', new sfab_FabricatedSObject(Account.class)); + System.assertNotEquals( null, fabricatedSObject.getParent('Account') ); + } + + @isTest + private static void setParent_whenParentOfParent_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Opportunity.class); + fabricatedSObject.setParent('Account.CreatedBy', new sfab_FabricatedSObject(User.class)); + System.assertNotEquals( null, fabricatedSObject.getParent('Account').getParent('CreatedBy') ); + } + + @isTest + private static void set_whenParent_expectFieldAddedToNodes() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Opportunity.class); + fabricatedSObject.set('Account', new sfab_FabricatedSObject(Account.class)); + System.assertNotEquals( null, fabricatedSObject.getParent('Account') ); + } + + @isTest + private static void setChildren_expectChildrenSet() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.setChildren('Opportunities', new List{ + new sfab_FabricatedSObject(Opportunity.class), + new sfab_FabricatedSObject(Opportunity.class) } ); + System.assertEquals( 2, fabricatedSObject.getNumberOfChildren('Opportunities') ); + } + + @isTest + private static void setChildren_whenChildrenOfParent_expectChildrenSet() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Opportunity.class); + fabricatedSObject.setChildren('Account.Contacts', new List{ + new sfab_FabricatedSObject(Contact.class), + new sfab_FabricatedSObject(Contact.class) } ); + + System.assertEquals( 2, fabricatedSObject.getParent('Account').getNumberOfChildren('Contacts') ); + } + + @isTest + private static void set_whenChildren_expectChildSet() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.set('Opportunities', new List{ + new sfab_FabricatedSObject(Opportunity.class), + new sfab_FabricatedSObject(Opportunity.class) } ); + System.assertEquals( 2, fabricatedSObject.getNumberOfChildren('Opportunities') ); + } + + @isTest + private static void set_whenChildrenOfParent_expectChildSet() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Opportunity.class); + fabricatedSObject.set('Account.Contacts', new List{ + new sfab_FabricatedSObject(Contact.class), + new sfab_FabricatedSObject(Contact.class) } ); + + System.assertEquals( 2, fabricatedSObject.getParent('Account').getNumberOfChildren('Contacts') ); + } + + @isTest + private static void addChild_expectObjectsAdded() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.addChild('Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + fabricatedSObject.addChild('Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + System.assertEquals( 1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 2, fabricatedSObject.getNumberOfChildren( 'Opportunities' ) ); + } + + @isTest + private static void addChild_whenAParentsChildIsSpecified_expectObjectsAdded() { + sfab_FabricatedSObject fabricatedContact = new sfab_FabricatedSObject(Contact.class); + fabricatedContact.addChild('Account.Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + fabricatedContact.addChild('Account.Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + System.assertEquals( 1, fabricatedContact.getNumberOfNodes()); + + sfab_FabricatedSObject fabricatedAccount = fabricatedContact.getParent( 'Account' ); + System.assertEquals( 1, fabricatedAccount.getNumberOfNodes()); + System.assertEquals( 2, fabricatedAccount.getNumberOfChildren( 'Opportunities' ) ); + } + + @isTest + private static void add_expectObjectsAdded() { + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject(Account.class); + fabricatedSObject.add('Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + fabricatedSObject.add('Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + System.assertEquals( 1, fabricatedSObject.getNumberOfNodes()); + System.assertEquals( 2, fabricatedSObject.getNumberOfChildren( 'Opportunities' ) ); + } + + @isTest + private static void add_whenAParentsChildIsSpecified_expectObjectsAdded() { + sfab_FabricatedSObject fabricatedContact = new sfab_FabricatedSObject(Contact.class); + fabricatedContact.add('Account.Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + fabricatedContact.add('Account.Opportunities', new sfab_FabricatedSObject(Opportunity.class)); + System.assertEquals(1, fabricatedContact.getNumberOfNodes()); + + sfab_FabricatedSObject fabricatedAccount = fabricatedContact.getParent( 'Account' ); + System.assertEquals( 1, fabricatedAccount.getNumberOfNodes()); + System.assertEquals( 2, fabricatedAccount.getNumberOfChildren( 'Opportunities' ) ); + } + + @isTest + private static void setField_whenFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( Account.AccountNumber, 'Id-1' ); + } catch ( sfab_FabricatedSObject.FieldDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Contact.AccountNumber does not exist' ), + 'setField, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Invalid', 'Id-1' ); + } catch ( sfab_FabricatedSObject.FieldDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Invalid does not exist' ), + 'setField, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringFieldIsParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Account', 'Is a parent relationship' ); + } catch ( sfab_FabricatedSObject.FieldIsNotSimpleFieldException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Contact.Account cannot to set to a primitive, it is a parent relationship field' ), + 'setField, when field is a parent relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringFieldIsChildRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Contacts', 'Is a child relationship' ); + } catch ( sfab_FabricatedSObject.FieldIsNotSimpleFieldException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Contacts cannot to set to a primitive, it is a child relationship field' ), + 'setField, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringNavigatingAndFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Account.Invalid', 'Id-1' ); + } catch ( sfab_FabricatedSObject.FieldDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Invalid does not exist' ), + 'setField, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringNavigatingAndFieldIsParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Account.Owner', 'Is a parent relationship' ); + } catch ( sfab_FabricatedSObject.FieldIsNotSimpleFieldException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Owner cannot to set to a primitive, it is a parent relationship field' ), + 'setField, when field is a parent relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setField_whenStringNavigatingAndFieldIsChildRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setField( 'Account.Contacts', 'Is a child relationship' ); + } catch ( sfab_FabricatedSObject.FieldIsNotSimpleFieldException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Contacts cannot to set to a primitive, it is a child relationship field' ), + 'setField, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Account', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The parent relationship Account.Account does not exist' ), + 'setParent, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Name', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotParentRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a parent relationship' ), + 'setParent, when field is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldIsAChildRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Contacts', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotParentRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Contacts is not a parent relationship' ), + 'setParent, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldIsADifferentSobject_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Account', new sfab_FabricatedSObject( Contact.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsADifferentTypeException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Contact.Account is Account, not Contact' ), + 'setParent, when field is a different SObject, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldIsPolymorphicAndADifferentSobject_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Task.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Owner', new sfab_FabricatedSObject( Contact.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsADifferentTypeException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Task.Owner is Group,User, not Contact' ), + 'setParent, when field is polymorphic and for a different SObject, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldIsPolymorphicAndACorrectSobject_expectNoException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Task.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Owner', new sfab_FabricatedSObject( User.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsADifferentTypeException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( '', exceptionMessage, 'setParent, when field is polymorphic and the passed SObject included, will not throw an exception (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldOnParentDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Account.NotValid', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The parent relationship Account.NotValid does not exist' ), + 'setParent, when field on the parent does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldOnParentIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Account.Name', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotParentRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a parent relationship' ), + 'setParent, when field on the parent is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setParent_whenFieldOnParentIsAChildRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setParent( 'Account.Opportunities', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotParentRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Opportunities is not a parent relationship' ), + 'setParent, when field on the parent is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Accounts', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.ChildRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The child relationship Account.Accounts does not exist' ), + 'setChildren, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Name', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a child relationship' ), + 'setChildren, when field is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldIsAParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Account', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Contact.Account is not a child relationship' ), + 'setChildren, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldIsADifferentSobject_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Contacts', new List{ + new sfab_FabricatedSObject( User.class ) + }); + } catch ( sfab_FabricatedSObject.FieldIsADifferentTypeException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The relationship Account.Contacts is Contact, not User' ), + 'setChildren, when field is a different SObject, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldOnParentDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Account.Accounts', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.ChildRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The child relationship Account.Accounts does not exist' ), + 'setChildren, when field on parent does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldOnParentIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Account.Name', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a child relationship' ), + 'setChildren, when field is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void setChildren_whenFieldOnParentIsAParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.setChildren( 'Account.Owner', new List{ + new sfab_FabricatedSObject( Account.class ) + }); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Owner is not a child relationship' ), + 'setChildren, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Accounts', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.ChildRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The child relationship Account.Accounts does not exist' ), + 'addChild, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldIsADifferentObjectType_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Contacts', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsADifferentTypeException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The relationship Account.Contacts is Contact, not Account' ), + 'addChild, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Name', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a child relationship' ), + 'addChild, when field is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldIsAParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Account', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Contact.Account is not a child relationship' ), + 'addChild, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldOnParentDoesNotExist_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Account.Accounts', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.ChildRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The child relationship Account.Accounts does not exist' ), + 'addChild, when field on parent does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldOnParentIsASimpleField_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Account.Name', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Name is not a child relationship' ), + 'addChild, when field is a simple field, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void addChild_whenFieldOnParentIsAParentRelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Contact.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.addChild( 'Account.Owner', new sfab_FabricatedSObject( Account.class ) ); + } catch ( sfab_FabricatedSObject.FieldIsNotChildRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'The field Account.Owner is not a child relationship' ), + 'addChild, when field is a child relationship, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void set_whenStringFieldForParentNotARelationship_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Account.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.set( 'Name.NotARelationship', 'name is not a relationship' ); + } catch ( sfab_FabricatedSObject.FieldIsNotParentRelationshipException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'Account.Name is not a parent relationship' ), 'set, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void set_whenStringFieldForParentPolymorphic_expectException() { + + sfab_FabricatedSObject fabricatedSObject = new sfab_FabricatedSObject( Task.class ); + + String exceptionMessage = ''; + try { + fabricatedSObject.set( 'Owner.Username', 'cannot create polymorphic ones automatically' ); + } catch ( sfab_FabricatedSObject.ParentRelationshipObjectCannotBeAutoCreatedException e ) { + exceptionMessage = e.getMessage(); + } + System.assert( exceptionMessage.contains( 'Could not auto-assign an object for the field Owner' ), 'set, when field does not exist, will throw an exception with a useful message (got "' + exceptionMessage + '")' ); + } + + @isTest + private static void toSObject_whenBlobSetOnContentVersion_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.set( 'VersionData', Blob.valueOf( 'abc' ) ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetOnAttachment_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( Attachment.class ); + fabricated.set( 'Body', Blob.valueOf( 'abc' ) ); + Attachment sobj = (Attachment)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.Body ); + } + + @isTest + private static void toSObject_whenBlobSetOnDocument_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( Document.class ); + fabricated.set( 'Body', Blob.valueOf( 'abc' ) ); + Document sobj = (Document)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.Body ); + } + + @isTest + private static void toSObject_whenBlobSetOnStaticResource_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( StaticResource.class ); + fabricated.set( 'Body', Blob.valueOf( 'abc' ) ); + StaticResource sobj = (StaticResource)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.Body ); + } + + @isTest + private static void toSObject_whenBlobSetOnMailMergeTemplate_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( MailMergeTemplate.class ); + fabricated.set( 'Body', Blob.valueOf( 'abc' ) ); + MailMergeTemplate sobj = (MailMergeTemplate)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.Body ); + } + + @isTest + private static void toSObject_whenBlobSetOnEventLogFile_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( EventLogFile.class ); + fabricated.set( 'LogFile', Blob.valueOf( 'abc' ) ); + EventLogFile sobj = (EventLogFile)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.LogFile ); + } + + @isTest + private static void toSObject_whenBlobSetOnMobileApplicationDetail_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( MobileApplicationDetail.class ); + fabricated.set( 'ApplicationBinary', Blob.valueOf( 'abc' ) ); + MobileApplicationDetail sobj = (MobileApplicationDetail)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.ApplicationBinary ); + } + + @isTest + private static void toSObject_whenBlobSetViaField_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.set( ContentVersion.VersionData, Blob.valueOf( 'abc' ) ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetViaSetField_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.setField( 'VersionData', Blob.valueOf( 'abc' ) ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetViaSetFieldWithField_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.setField( ContentVersion.VersionData, Blob.valueOf( 'abc' ) ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetToString_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.set( 'VersionData', 'abc' ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetToEmptyString_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.set( 'VersionData', '' ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( '' ), sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetToNull_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentVersion.class ); + fabricated.set( 'VersionData', (String)null ); + ContentVersion sobj = (ContentVersion)fabricated.toSObject(); + System.assertEquals( null, sobj.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetOnParent_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = new sfab_FabricatedSObject( ContentDocumentLink.class ); + fabricated.set( 'ContentDocument.LatestPublishedVersion.VersionData', Blob.valueOf( 'abc' ) ); + ContentDocumentLink sobj = (ContentDocumentLink)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.ContentDocument.LatestPublishedVersion.VersionData ); + } + + @isTest + private static void toSObject_whenBlobSetAndChildren_expectBlobValueSet() { + sfab_FabricatedSObject fabricated = + new sfab_FabricatedSObject( ContentDocument.class ) + .add( 'ContentVersions', new sfab_FabricatedSObject( ContentVersion.class ) + .set( 'VersionData', Blob.valueOf( 'def' ) ) ) + .add( 'ContentVersions', new sfab_FabricatedSObject( ContentVersion.class ) + .set( 'VersionData', Blob.valueOf( 'ghi' ) ) ) + .add( 'ContentVersions', new sfab_FabricatedSObject( ContentVersion.class ) + .set( 'VersionData', Blob.valueOf( 'abc' ) ) ); + + ContentDocument sobj = (ContentDocument)fabricated.toSObject(); + System.assertEquals( Blob.valueOf( 'def' ), sobj.ContentVersions[0].VersionData ); + System.assertEquals( Blob.valueOf( 'ghi' ), sobj.ContentVersions[1].VersionData ); + System.assertEquals( Blob.valueOf( 'abc' ), sobj.ContentVersions[2].VersionData ); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // sfab_FabricatedSObject.sfab_FieldValuePairNode tests + // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @isTest + private static void fieldNode_serialize_whenPassedTrue_expectMap() { + sfab_FabricatedSObject.sfab_FieldValuePairNode node = new sfab_FabricatedSObject.sfab_FieldValuePairNode(Account.Name, 'Foo'); + Map builtNode = node.serialize( true ); + + System.assertEquals(new Map { 'Name' => 'Foo' }, builtNode); + } + @isTest + private static void fieldNode_serialize_whenPassedFalse_expectMap() { + sfab_FabricatedSObject.sfab_FieldValuePairNode node = new sfab_FabricatedSObject.sfab_FieldValuePairNode(Account.Name, 'Foo'); + Map builtNode = node.serialize( false ); + + System.assertEquals(new Map { 'Name' => 'Foo' }, builtNode); + } + + @isTest + private static void fieldNode_getName_expectNameFromField() { + sfab_FabricatedSObject.sfab_FieldValuePairNode node = new sfab_FabricatedSObject.sfab_FieldValuePairNode(Account.Name, 'Foo'); + System.assertEquals( 'Name', node.getName() ); + } + + @isTest + private static void fieldNode_getName_whenNoField_expectNull() { + sfab_FabricatedSObject.sfab_FieldValuePairNode node = new sfab_FabricatedSObject.sfab_FieldValuePairNode(null, 'Foo'); + System.assertEquals( null, node.getName() ); + } + + @isTest + private static void fieldNode_getValue_expectValueFromField() { + sfab_FabricatedSObject.sfab_FieldValuePairNode node = new sfab_FabricatedSObject.sfab_FieldValuePairNode(Account.Name, 'Foo'); + System.assertEquals( 'Foo', node.getValue() ); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // sfab_FabricatedSObject.sfab_FabricatedSObject.sfab_ChildRelationshipNode tests + // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @isTest + private static void childNode_serialize_whenPassedTrue_expectSerializeInvokedOnChildren() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { child1, child2 }); + + node.serialize( true ); + + System.assert(child1.serializeInvoked); + System.assert(child1.serializeInvokedWith); + System.assert(child2.serializeInvoked); + System.assert(child2.serializeInvokedWith); + } + @isTest + private static void childNode_serialize_whenPassedFalse_expectSerializeInvokedOnChildren() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { child1, child2 }); + + node.serialize( false ); + + System.assert(child1.serializeInvoked); + System.assert(!child1.serializeInvokedWith); + System.assert(child2.serializeInvoked); + System.assert(!child2.serializeInvokedWith); + } + + @isTest + private static void childNode_serialize_whenPassedTrue_expectDoneMapKey() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List()); + + Map builtNode = node.serialize( true ); + + System.assert(((Map)builtNode.get('Accounts__r')).containsKey('done')); + System.assert((Boolean)((Map)builtNode.get('Accounts__r')).get('done')); + } + + @isTest + private static void childNode_serialize_whenPassedTrue_expectTotalSizeMapKey() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { + new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class), + new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class) + }); + + Map builtNode = node.serialize( true ); + + System.assert(((Map)builtNode.get('Accounts__r')).containsKey('totalSize')); + System.assertEquals(2, ((Map)builtNode.get('Accounts__r')).get('totalSize')); + } + + @isTest + private static void childNode_serialize_whenPassedTrue_expectSerializedChildrenMap() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-1' }); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-2' }); + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { child1, child2 }); + + List> serializedChildren = new List>(); + serializedChildren.add(child1.serialize()); + serializedChildren.add(child2.serialize()); + + Map builtNode = node.serialize( true ); + + System.assertEquals(serializedChildren, ((Map)builtNode.get('Accounts__r')).get('records')); + } + + @isTest + private static void childNode_serialize_whenPassedFalse_expectDoneMapKey() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List()); + + Map builtNode = node.serialize( false ); + + System.assert(((Map)builtNode.get('Accounts__r')).containsKey('done')); + System.assert((Boolean)((Map)builtNode.get('Accounts__r')).get('done')); + } + + @isTest + private static void childNode_serialize_whenPassedFalse_expectTotalSizeMapKey() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { + new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class), + new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class) + }); + + Map builtNode = node.serialize( false ); + + System.assert(((Map)builtNode.get('Accounts__r')).containsKey('totalSize')); + System.assertEquals(2, ((Map)builtNode.get('Accounts__r')).get('totalSize')); + } + + @isTest + private static void childNode_serialize_whenPassedFalse_expectSerializedChildrenMap() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-1' }); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-2' }); + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { child1, child2 }); + + List> serializedChildren = new List>(); + serializedChildren.add(child1.serialize()); + serializedChildren.add(child2.serialize()); + + Map builtNode = node.serialize( false ); + + System.assertEquals(serializedChildren, ((Map)builtNode.get('Accounts__r')).get('records')); + } + + @isTest + private static void childNode_getNumberOfChildren_expectNumberOfChildrenAdded() { + + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-1' }); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-2' }); + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', new List { child1, child2 }); + + System.assertEquals(2, node.getNumberOfChildren()); + } + + @isTest + private static void childNode_getChildren_expectTheChildren() { + + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child1 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-1' }); + sfab_FabricatedSObject.sfab_FabricatedSObjectStub child2 = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-2' }); + List children = new List { child1, child2 }; + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r', children); + + System.assertEquals(children, node.getChildren()); + } + + @isTest + private static void childNode_getName_expectTheName() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r'); + System.assertEquals('Accounts__r', node.getName()); + } + + @isTest + private static void childNode_constructor_withNoChildren_expectEmptyChildren() { + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r'); + System.assertEquals(0, node.getNumberOfChildren()); + } + + @isTest + private static void childNode_addChild_expectAddsAChildNode() { + + sfab_FabricatedSObject.sfab_ChildRelationshipNode node = new sfab_FabricatedSObject.sfab_ChildRelationshipNode('Accounts__r' ); + node.addChild( new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-1' } ) ); + node.addChild( new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo-2' } ) ); + + System.assertEquals(2, node.getNumberOfChildren()); + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // sfab_FabricatedSObject.sfab_ParentRelationshipNode tests + // + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @isTest + private static void parentNode_serialize_whenPassedTrue_expectSerializeInvokedOnParent() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + node.serialize( true ); + + System.assert(fabricatedParent.serializeInvoked); + System.assert(fabricatedParent.serializeInvokedWith); + } + + @isTest + private static void parentNode_serialize_whenPassedFalse_expectSerializeInvokedOnParent() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + node.serialize( false ); + + System.assert(fabricatedParent.serializeInvoked); + System.assert(!fabricatedParent.serializeInvokedWith); + } + + @isTest + private static void parentNode_serialize_whenPassedFalse_expectSerializedParentMap() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo' }); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + Map builtNode = node.serialize( false ); + + System.assertEquals(new Map { 'Account__r' => fabricatedParent.serialize() }, builtNode); + } + + @isTest + private static void parentNode_serialize_whenPassedTrue_expectSerializedParentMap() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo' }); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + Map builtNode = node.serialize( true ); + + System.assertEquals(new Map(), builtNode); + } + + @isTest + private static void parentNode_getName_expectParentReturned() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo' }); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + System.assertEquals( 'Account__r', node.getName() ); + } + + @isTest + private static void parentNode_getParent_expectParentReturned() { + sfab_FabricatedSObject.sfab_FabricatedSObjectStub fabricatedParent = new sfab_FabricatedSObject.sfab_FabricatedSObjectStub(Account.class, new Map { 'Name' => 'Foo' }); + sfab_FabricatedSObject.sfab_ParentRelationshipNode node = new sfab_FabricatedSObject.sfab_ParentRelationshipNode('Account__r', fabricatedParent); + + System.assertEquals( fabricatedParent, node.getParent() ); + } + + @isTest + private static void getSobjectType_whenTypeIsSet_willReturnTheTypeAsAnSObjectType() { + sfab_FabricatedSObject obj = new sfab_FabricatedSObject( Contact.class ); + System.assertEquals( Contact.sobjectType, obj.getSobjectType(), 'getSobjectType, when type is set, will return the type as an SObjectType' ); + } + + @isTest + private static void getSobjectName_whenTypeIsSet_willReturnTheTypeAsAString() { + sfab_FabricatedSObject obj = new sfab_FabricatedSObject( Contact.class ); + System.assertEquals( 'Contact', obj.getSobjectName(), 'getSobjectName, when type is set, will return the type as a String' ); + } +} \ No newline at end of file diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls-meta.xml b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_FabricatedSObjectTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active + diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls new file mode 100644 index 00000000000..452adfa3a56 --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls @@ -0,0 +1,323 @@ +@isTest +public with sharing class sfab_ObjectDescriberTest { + + @isTest + private static void getField_whenGivenAFieldThatExist_expectTheField() { + SObjectField field = new sfab_ObjectDescriber() + .getField( 'Contact', 'Id' ); + System.assertEquals( 'Id', field.getDescribe().getName() ); + } + + @isTest + private static void getField_whenGivenAFieldThatDoesNotExist_expectNull() { + SObjectField field = new sfab_ObjectDescriber() + .getField( 'Contact', 'NotValid' ); + System.assertEquals( null, field ); + } + + @isTest + private static void getField_whenGivenAnObjectThatDoesNotExist_expectNull() { + SObjectField field = new sfab_ObjectDescriber() + .getField( 'NotValid', 'Id' ); + System.assertEquals( null, field ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenASimpleField_expectTrue() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'Contact', 'AccountId' ); + System.assert( simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenParentRelationshipField_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'Contact', 'Account' ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenChildRelationship_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'Account', 'Contacts' ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenInvalidField_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'Account', 'NotAField' ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenInvalidObject_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'NotAnObject', 'Id' ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenANullObject_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( null, 'Id' ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasSimpleFieldNamed_whenGivenANullField_expectFalse() { + Boolean simpleField = new sfab_ObjectDescriber() + .objectHasSimpleFieldNamed( 'Account', null ); + System.assert( !simpleField ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenAValidRelationship_expectReturnTrue() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( 'Contact', 'Account' ); + System.assert( hasParentRelationshipNamed ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenAPolymorphicRelationship_expectReturnTrue() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( 'Task', 'Owner' ); + System.assert( hasParentRelationshipNamed ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenAnInvalidRelationship_expectReturnFalse() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( 'Contact', 'NotARelationship' ); + System.assert( ! hasParentRelationshipNamed ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenAnInvalidObject_expectReturnFalse() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( 'NotAnObject', 'Account' ); + System.assert( ! hasParentRelationshipNamed ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenANullObject_expectReturnFalse() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( null, 'Account' ); + System.assert( ! hasParentRelationshipNamed ); + } + + @isTest + private static void objectHasParentRelationshipNamed_whenGivenANullRelationship_expectReturnFalse() { + Boolean hasParentRelationshipNamed = new sfab_ObjectDescriber() + .objectHasParentRelationshipNamed( 'Contact', null ); + System.assert( ! hasParentRelationshipNamed ); + } + + @isTest + private static void getObjectTypesForParentRelationship_whenGivenAParentRelationshipName_expectObjectItRelatesTo() { + List targetObjectTypes = new sfab_ObjectDescriber() + .getObjectTypesForParentRelationship( 'Contact', 'Account' ); + System.assertEquals( 1, targetObjectTypes.size() ); + System.assertEquals( 'Account', targetObjectTypes[0] ); + } + + @isTest + private static void getObjectTypesForParentRelationship_whenGivenAPolymorphicParentRelationshipName_expectAllObjectsItRelatesTo() { + List targetObjectTypes = new sfab_ObjectDescriber() + .getObjectTypesForParentRelationship( 'Task', 'Owner' ); + System.assertEquals( 2, targetObjectTypes.size() ); + System.assertEquals( 'Group', targetObjectTypes[0] ); + System.assertEquals( 'User', targetObjectTypes[1] ); + } + + @isTest + private static void getObjectTypesForParentRelationship_whenGivenAnInvalidRelationshipName_expectEmptyList() { + List targetObjectTypes = new sfab_ObjectDescriber() + .getObjectTypesForParentRelationship( 'Contact', 'AccountId' ); + System.assertEquals( 0, targetObjectTypes.size() ); + } + + @isTest + private static void getObjectTypesForParentRelationship_whenGivenAnInvalidObjectName_expectEmptyList() { + List targetObjectTypes = new sfab_ObjectDescriber() + .getObjectTypesForParentRelationship( 'Invalid', 'Account' ); + System.assertEquals( 0, targetObjectTypes.size() ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenAValidRelationship_expectAFabObject() { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( 'Contact', 'Owner' ); + System.assertEquals( 'User', fabricatedObject.getSobjectName() ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenAPolymorphicRelationship_expectAnException() { + + String exceptionMessage; + try { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( 'Task', 'Owner' ); + } catch ( sfab_ObjectDescriber.ParentRelationshipObjectCannotBeAutoCreatedException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( 'Cannot automatically create a Fabricated Object for the relationship Task.Owner as it is polymorphic and so not possible to automatically ascertain which SObject to use', exceptionMessage ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenAnInvalidRelationship_expectAnException() { + + String exceptionMessage; + try { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( 'Contact', 'Invalid' ); + } catch ( sfab_ObjectDescriber.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( 'Cannot automatically create a Fabricated Object for the relationship Contact.Invalid because the relationship does not exist', exceptionMessage ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenAnInvalidObject_expectAnException() { + + String exceptionMessage; + try { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( 'Invalid', 'Account' ); + } catch ( sfab_ObjectDescriber.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( 'Cannot automatically create a Fabricated Object for the relationship Invalid.Account because the object type does not exist', exceptionMessage ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenANullRelationship_expectAnException() { + + String exceptionMessage; + try { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( 'Contact', null ); + } catch ( sfab_ObjectDescriber.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( 'Cannot automatically create a Fabricated Object for the relationship because the relationship name has not been specified', exceptionMessage ); + } + + @isTest + private static void buildFabricatedObjectForRelationship_whenGivenANullObject_expectAnException() { + + String exceptionMessage; + try { + sfab_FabricatedSObject fabricatedObject = new sfab_ObjectDescriber() + .buildFabricatedObjectForRelationship( null, 'Account' ); + } catch ( sfab_ObjectDescriber.ParentRelationshipDoesNotExistException e ) { + exceptionMessage = e.getMessage(); + } + System.assertEquals( 'Cannot automatically create a Fabricated Object for the relationship because the object name has not been specified', exceptionMessage ); + } + + @isTest + private static void objectHasChildRelationshipNamed_whenGivenAValidRelationship_expectReturnTrue() { + Boolean hasChildRelationshipNamed = new sfab_ObjectDescriber() + .objectHasChildRelationshipNamed( 'Account', 'Contacts' ); + System.assert( hasChildRelationshipNamed ); + } + + @isTest + private static void objectHasChildRelationshipNamed_whenGivenAnInvalidRelationship_expectReturnFalse() { + Boolean hasChildRelationshipNamed = new sfab_ObjectDescriber() + .objectHasChildRelationshipNamed( 'Account', 'NotARelationship' ); + System.assert( ! hasChildRelationshipNamed ); + } + + @isTest + private static void objectHasChildRelationshipNamed_whenGivenAnInvalidObject_expectReturnFalse() { + Boolean hasChildRelationshipNamed = new sfab_ObjectDescriber() + .objectHasChildRelationshipNamed( 'NotAnObject', 'Account' ); + System.assert( ! hasChildRelationshipNamed ); + } + + @isTest + private static void objectHasChildRelationshipNamed_whenGivenANullObject_expectReturnFalse() { + Boolean hasChildRelationshipNamed = new sfab_ObjectDescriber() + .objectHasChildRelationshipNamed( null, 'Account' ); + System.assert( ! hasChildRelationshipNamed ); + } + + @isTest + private static void objectHasChildRelationshipNamed_whenGivenANullRelationship_expectReturnFalse() { + Boolean hasChildRelationshipNamed = new sfab_ObjectDescriber() + .objectHasChildRelationshipNamed( 'Contact', null ); + System.assert( ! hasChildRelationshipNamed ); + } + + @isTest + private static void getObjectTypeForChildRelationship_whenGivenAChildRelationshipName_expectObjectItRelatesTo() { + String targetObjectType = new sfab_ObjectDescriber() + .getObjectTypeForChildRelationship( 'Account', 'Contacts' ); + System.assertEquals( 'Contact', targetObjectType ); + } + + @isTest + private static void getObjectTypeForChildRelationship_whenGivenAnInvalidRelationshipName_expectNull() { + String targetObjectType = new sfab_ObjectDescriber() + .getObjectTypeForChildRelationship( 'Account', 'Contactylike' ); + System.assertEquals( null, targetObjectType ); + } + + @isTest + private static void getObjectTypeForChildRelationship_whenGivenAnInvalidObjectName_expectNull() { + String targetObjectType = new sfab_ObjectDescriber() + .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 diff --git a/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls-meta.xml b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls-meta.xml new file mode 100644 index 00000000000..f928c8e56bc --- /dev/null +++ b/framework/default/sobject-fabricator/classes/tests/sfab_ObjectDescriberTest.cls-meta.xml @@ -0,0 +1,5 @@ + + + 53.0 + Active +