Post Closed as "Duplicate of…" by Pranay Jaiswal, glls, David Cheng, EazyE, Himanshu of
5 edited test class
source | link
@istest
static void  DocumentAttach(){
 //...order creation  with a list of 2 orders for testing
List<Order> orders = new List<Order>();
insert orders;

String before = 'Testing base 64 encode';            
Blob beforeblob = Blob.valueOf(before);
ContentVersion cv = new ContentVersion();
cv.title = 'test content trigger';      
cv.PathOnClient ='test';           
cv.VersionData =beforeblob;  
cv.firstPublishLocationId = o.Id;     
insert cv;  //this 
insertinsertion cv;fires trigger on content document link 3 times       

List<ContentDocumentLink> cvLinks = new List<ContentDocumentLink> ();

Id cdID = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id].ContentDocumentId;

ContentDocumentLinkfor(Integer cdli = new0; ContentDocumentLink();
cdl.ContentDocumentIdi =< cdID10; ;i++){
cdl.LinkedEntityId = o.Id;
cdl.ShareType = 'V';
cvlinks.add(cdl);

ContentDocumentLink cdl2cdl = new ContentDocumentLink();
cdl2  cdl.ContentDocumentId = cdID ;
cdl2  cdl.LinkedEntityId = o2orders[i].Id;
cdl2  cdl.ShareType = 'V';
  cvlinks.add(cdl2cdl);
}

Test.startTest(); 
insert cvlinks; //this fires the trigger 3 times
Test.stopTest();
}
@istest
static void  DocumentAttach(){
 //...order creation  with a list of 2 orders for testing
List<Order> orders = new List<Order>();
insert orders;

String before = 'Testing base 64 encode';            
Blob beforeblob = Blob.valueOf(before);
ContentVersion cv = new ContentVersion();
cv.title = 'test content trigger';      
cv.PathOnClient ='test';           
cv.VersionData =beforeblob;  
cv.firstPublishLocationId = o.Id;         
insert cv;         

List<ContentDocumentLink> cvLinks = new List<ContentDocumentLink> ();

Id cdID = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id].ContentDocumentId;

ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = cdID ;
cdl.LinkedEntityId = o.Id;
cdl.ShareType = 'V';
cvlinks.add(cdl);

ContentDocumentLink cdl2 = new ContentDocumentLink();
cdl2.ContentDocumentId = cdID ;
cdl2.LinkedEntityId = o2.Id;
cdl2.ShareType = 'V';
cvlinks.add(cdl2);


Test.startTest(); 
insert cvlinks; 
Test.stopTest();
}
@istest
static void  DocumentAttach(){
 //...order creation  with a list of 2 orders for testing
List<Order> orders = new List<Order>();
insert orders;

String before = 'Testing base 64 encode';            
Blob beforeblob = Blob.valueOf(before);
ContentVersion cv = new ContentVersion();
cv.title = 'test content trigger';      
cv.PathOnClient ='test';           
cv.VersionData =beforeblob;         
insert cv;  //this insertion fires trigger on content document link 3 times       

List<ContentDocumentLink> cvLinks = new List<ContentDocumentLink> ();

Id cdID = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id].ContentDocumentId;

for(Integer i = 0; i < 10; i++){
   ContentDocumentLink cdl = new ContentDocumentLink();
  cdl.ContentDocumentId = cdID ;
  cdl.LinkedEntityId = orders[i].Id;
  cdl.ShareType = 'V';
  cvlinks.add(cdl);
}

Test.startTest(); 
insert cvlinks; //this fires the trigger 3 times
Test.stopTest();
}
4 Added new test class
source | link

EDIT Following the hint from @cropredy I've tried to write a test class with a mass insertion of multiple ContentDocumentLink but I'm not able to achieve it, as even with only 2 objects I get exception

System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, Document with ID: 0690D000000iIIQ is already linked with the entity with ID: 8010D000000QMqJ: [LinkedEntityId]

Code is as follow:

@istest
static void  DocumentAttach(){
 //...order creation  with a list of 2 orders for testing
List<Order> orders = new List<Order>();
insert orders;

String before = 'Testing base 64 encode';            
Blob beforeblob = Blob.valueOf(before);
ContentVersion cv = new ContentVersion();
cv.title = 'test content trigger';      
cv.PathOnClient ='test';           
cv.VersionData =beforeblob;  
cv.firstPublishLocationId = o.Id;         
insert cv;         

List<ContentDocumentLink> cvLinks = new List<ContentDocumentLink> ();

Id cdID = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id].ContentDocumentId;

ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = cdID ;
cdl.LinkedEntityId = o.Id;
cdl.ShareType = 'V';
cvlinks.add(cdl);

ContentDocumentLink cdl2 = new ContentDocumentLink();
cdl2.ContentDocumentId = cdID ;
cdl2.LinkedEntityId = o2.Id;
cdl2.ShareType = 'V';
cvlinks.add(cdl2);


Test.startTest(); 
insert cvlinks; 
Test.stopTest();
}

Any clue?

EDIT Following the hint from @cropredy I've tried to write a test class with a mass insertion of multiple ContentDocumentLink but I'm not able to achieve it, as even with only 2 objects I get exception

System.DmlException: Insert failed. First exception on row 0; first error: FIELD_INTEGRITY_EXCEPTION, Document with ID: 0690D000000iIIQ is already linked with the entity with ID: 8010D000000QMqJ: [LinkedEntityId]

Code is as follow:

@istest
static void  DocumentAttach(){
 //...order creation  with a list of 2 orders for testing
List<Order> orders = new List<Order>();
insert orders;

String before = 'Testing base 64 encode';            
Blob beforeblob = Blob.valueOf(before);
ContentVersion cv = new ContentVersion();
cv.title = 'test content trigger';      
cv.PathOnClient ='test';           
cv.VersionData =beforeblob;  
cv.firstPublishLocationId = o.Id;         
insert cv;         

List<ContentDocumentLink> cvLinks = new List<ContentDocumentLink> ();

Id cdID = [SELECT Id, ContentDocumentId FROM ContentVersion WHERE Id =: cv.Id].ContentDocumentId;

ContentDocumentLink cdl = new ContentDocumentLink();
cdl.ContentDocumentId = cdID ;
cdl.LinkedEntityId = o.Id;
cdl.ShareType = 'V';
cvlinks.add(cdl);

ContentDocumentLink cdl2 = new ContentDocumentLink();
cdl2.ContentDocumentId = cdID ;
cdl2.LinkedEntityId = o2.Id;
cdl2.ShareType = 'V';
cvlinks.add(cdl2);


Test.startTest(); 
insert cvlinks; 
Test.stopTest();
}

Any clue?

3 Fixed a little code formatting, forcing syntax highlighting
source | link
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
       ContentDocumentLinkTriggerHandler.afterInsert(Trigger.oldMap, Trigger.newMap);
    }
}
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
       ContentDocumentLinkTriggerHandler.afterInsert(Trigger.oldMap, Trigger.newMap);
    }
}

Trigger handler

public class ContentDocumentLinkTriggerHandler { public static void afterInsert(Map oldMap, Map newMap){

    Set<Id> cvIds = new Set<Id>();
    for (ContentDocumentLink dLink : newMap.values()) {
        cvIds.add(dlink.contentDocumentId);
    }

    Map<Id, ContentVersion> docVersions = new Map<Id, ContentVersion>([SELECT contentDocumentId, firstPublishLocationId, Id
                                                  FROM ContentVersion
                                                  WHERE contentDocumentId IN : cvIds LIMIT 1]); 

    List<Id> idOrders = new List<Id>();
    List<Order> ordersToUpdate = new List<Order>();
    for(ContentVersion cv : docVersions.values()){
        idOrders.add(cv.firstPublishLocationId);
    }

    Map<Id,Order> updateOrder = new Map<Id,Order>([SELECT Id, customField FROM Order WHERE Id IN :idOrders ]);

    if(updateOrder.size() >0){
        for(Order o : updateOrder.values()){
                o.customField = 'custom';
                ordersToUpdate.add(o);
        }
    }
    update ordersToUpdate;
}

}

public class ContentDocumentLinkTriggerHandler {
    public static void afterInsert(Map<Id, ContentDocumentLink> oldMap, Map<Id, ContentDocumentLink> newMap){

        Set<Id> cvIds = new Set<Id>();
        for (ContentDocumentLink dLink : newMap.values()) {
            cvIds.add(dlink.contentDocumentId);
        }

        Map<Id, ContentVersion> docVersions = new Map<Id, ContentVersion>([SELECT contentDocumentId, firstPublishLocationId, Id
                                                      FROM ContentVersion
                                                      WHERE contentDocumentId IN : cvIds LIMIT 1]); 

        List<Id> idOrders = new List<Id>();
        List<Order> ordersToUpdate = new List<Order>();
        for(ContentVersion cv : docVersions.values()){
            idOrders.add(cv.firstPublishLocationId);
        }

        Map<Id,Order> updateOrder = new Map<Id,Order>([SELECT Id, customField FROM Order WHERE Id IN :idOrders ]);

        if(updateOrder.size() >0){
            for(Order o : updateOrder.values()){
                    o.customField = 'custom';
                    ordersToUpdate.add(o);
            }
        }
        update ordersToUpdate;
    }
}
public class ContentDocumentTriggerTest {   
    @istest
    static void  testingDoc(){
      ...//order creation
      List<ContentVersion> contentList = new List<contentVersion> ();
      for(Integer i = 0; i < 10; i++){
        ContentVersion contentVersion = new ContentVersion(
            Title = 'Penguins',
            PathOnClient = 'Penguins.jpg',
            VersionData = Blob.valueOf('Test Content'),
            IsMajorVersion = true,
            firstPublishLocationId = o.Id 

          );
          contentList.add(contentVersion);
      }


      Test.startTest();
      insert contentList;  //This insert fires 10 times the trigger
      Test.stopTest();
 }
public class ContentDocumentTriggerTest {   
    @istest
    static void  testingDoc(){
      ...//order creation
      List<ContentVersion> contentList = new List<contentVersion> ();
      for(Integer i = 0; i < 10; i++){
        ContentVersion contentVersion = new ContentVersion(
            Title = 'Penguins',
            PathOnClient = 'Penguins.jpg',
            VersionData = Blob.valueOf('Test Content'),
            IsMajorVersion = true,
            firstPublishLocationId = o.Id 

          );
          contentList.add(contentVersion);
      }


      Test.startTest();
      insert contentList;  //This insert fires 10 times the trigger
      Test.stopTest();
 }
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
       ContentDocumentLinkTriggerHandler.afterInsert(Trigger.oldMap, Trigger.newMap);
    }
}

Trigger handler

public class ContentDocumentLinkTriggerHandler { public static void afterInsert(Map oldMap, Map newMap){

    Set<Id> cvIds = new Set<Id>();
    for (ContentDocumentLink dLink : newMap.values()) {
        cvIds.add(dlink.contentDocumentId);
    }

    Map<Id, ContentVersion> docVersions = new Map<Id, ContentVersion>([SELECT contentDocumentId, firstPublishLocationId, Id
                                                  FROM ContentVersion
                                                  WHERE contentDocumentId IN : cvIds LIMIT 1]); 

    List<Id> idOrders = new List<Id>();
    List<Order> ordersToUpdate = new List<Order>();
    for(ContentVersion cv : docVersions.values()){
        idOrders.add(cv.firstPublishLocationId);
    }

    Map<Id,Order> updateOrder = new Map<Id,Order>([SELECT Id, customField FROM Order WHERE Id IN :idOrders ]);

    if(updateOrder.size() >0){
        for(Order o : updateOrder.values()){
                o.customField = 'custom';
                ordersToUpdate.add(o);
        }
    }
    update ordersToUpdate;
}

}

public class ContentDocumentTriggerTest {   
    @istest
    static void  testingDoc(){
      ...//order creation
      List<ContentVersion> contentList = new List<contentVersion> ();
      for(Integer i = 0; i < 10; i++){
        ContentVersion contentVersion = new ContentVersion(
            Title = 'Penguins',
            PathOnClient = 'Penguins.jpg',
            VersionData = Blob.valueOf('Test Content'),
            IsMajorVersion = true,
            firstPublishLocationId = o.Id 

          );
          contentList.add(contentVersion);
      }


      Test.startTest();
      insert contentList;  //This insert fires 10 times the trigger
      Test.stopTest();
 }
trigger ContentDocumentLinkTrigger on ContentDocumentLink (after insert) {
    if(Trigger.isInsert && Trigger.isAfter){
       ContentDocumentLinkTriggerHandler.afterInsert(Trigger.oldMap, Trigger.newMap);
    }
}

Trigger handler

public class ContentDocumentLinkTriggerHandler {
    public static void afterInsert(Map<Id, ContentDocumentLink> oldMap, Map<Id, ContentDocumentLink> newMap){

        Set<Id> cvIds = new Set<Id>();
        for (ContentDocumentLink dLink : newMap.values()) {
            cvIds.add(dlink.contentDocumentId);
        }

        Map<Id, ContentVersion> docVersions = new Map<Id, ContentVersion>([SELECT contentDocumentId, firstPublishLocationId, Id
                                                      FROM ContentVersion
                                                      WHERE contentDocumentId IN : cvIds LIMIT 1]); 

        List<Id> idOrders = new List<Id>();
        List<Order> ordersToUpdate = new List<Order>();
        for(ContentVersion cv : docVersions.values()){
            idOrders.add(cv.firstPublishLocationId);
        }

        Map<Id,Order> updateOrder = new Map<Id,Order>([SELECT Id, customField FROM Order WHERE Id IN :idOrders ]);

        if(updateOrder.size() >0){
            for(Order o : updateOrder.values()){
                    o.customField = 'custom';
                    ordersToUpdate.add(o);
            }
        }
        update ordersToUpdate;
    }
}
public class ContentDocumentTriggerTest {   
    @istest
    static void  testingDoc(){
      ...//order creation
      List<ContentVersion> contentList = new List<contentVersion> ();
      for(Integer i = 0; i < 10; i++){
        ContentVersion contentVersion = new ContentVersion(
            Title = 'Penguins',
            PathOnClient = 'Penguins.jpg',
            VersionData = Blob.valueOf('Test Content'),
            IsMajorVersion = true,
            firstPublishLocationId = o.Id 

          );
          contentList.add(contentVersion);
      }


      Test.startTest();
      insert contentList;  //This insert fires 10 times the trigger
      Test.stopTest();
 }
2 edited grammar and added details
source | link
1
source | link