/** * ═══════════════════════════════════════════════════════════════════════════════ * DELETE TEST DATA - COMPREHENSIVE CLEANUP * Remove all test data matching common test data patterns * ═══════════════════════════════════════════════════════════════════════════════ * * PURPOSE: * Clean up test data created by TestDataFactory classes. * Targets common test data naming patterns across multiple objects. * * PATTERNS MATCHED: * • 'Test%' - Standard test prefix * • 'BulkTest%' - Bulk testing data * • 'BatchTest%' - Batch Apex testing data * • 'BulkData%' - Large volume test data * • 'BulkImport%' - Import testing data * • 'Hierarchy%' - Hierarchy testing data * • '%@testfactory.example.com' - Test email domain * • '%@bulktest.example.com' - Bulk test email domain * * ═══════════════════════════════════════════════════════════════════════════════ */ // ═══════════════════════════════════════════════════════════════════════════════ // CONFIGURATION // ═══════════════════════════════════════════════════════════════════════════════ Boolean previewOnly = true; // Set to false to actually delete Integer maxRecordsPerObject = 10000; // Patterns to match (add your custom test patterns here) List namePatterns = new List{ 'Test%', 'BulkTest%', 'BatchTest%', 'BulkData%', 'BulkImport%', 'Hierarchy%' }; List emailPatterns = new List{ '%@testfactory.example.com', '%@bulktest.example.com', '%@batchtest.example.com' }; // ═══════════════════════════════════════════════════════════════════════════════ // TRACKING // ═══════════════════════════════════════════════════════════════════════════════ Map recordCounts = new Map(); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('COMPREHENSIVE TEST DATA CLEANUP'); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('Mode: ' + (previewOnly ? 'PREVIEW ONLY' : '⚠️ DELETE MODE')); System.debug('Patterns: ' + namePatterns); System.debug('═══════════════════════════════════════════════════════════════'); System.debug(''); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 1: FIND TASKS (delete first - activity records) // ═══════════════════════════════════════════════════════════════════════════════ List tasksToDelete = [ SELECT Id, Subject, WhoId, WhatId FROM Task WHERE Subject LIKE 'Test%' OR Subject LIKE 'BulkTest%' OR Subject LIKE 'Hierarchy%' LIMIT :maxRecordsPerObject ]; recordCounts.put('Task', tasksToDelete.size()); System.debug('📋 Tasks found: ' + tasksToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 2: FIND EVENTS // ═══════════════════════════════════════════════════════════════════════════════ List eventsToDelete = [ SELECT Id, Subject, WhoId, WhatId FROM Event WHERE Subject LIKE 'Test%' OR Subject LIKE 'All Day Event%' OR Subject LIKE 'Recurring Event%' OR Subject LIKE 'Calendar Event%' LIMIT :maxRecordsPerObject ]; recordCounts.put('Event', eventsToDelete.size()); System.debug('📋 Events found: ' + eventsToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 3: FIND CASES // ═══════════════════════════════════════════════════════════════════════════════ List casesToDelete = [ SELECT Id, Subject, AccountId FROM Case WHERE Subject LIKE 'Test%' OR Subject LIKE 'Hierarchy Case%' LIMIT :maxRecordsPerObject ]; recordCounts.put('Case', casesToDelete.size()); System.debug('📋 Cases found: ' + casesToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 4: FIND OPPORTUNITIES // ═══════════════════════════════════════════════════════════════════════════════ List opportunitiesToDelete = [ SELECT Id, Name, AccountId FROM Opportunity WHERE Name LIKE 'Test%' OR Name LIKE 'BulkTest%' OR Name LIKE 'BatchTest%' OR Name LIKE 'Hierarchy%' LIMIT :maxRecordsPerObject ]; recordCounts.put('Opportunity', opportunitiesToDelete.size()); System.debug('📋 Opportunities found: ' + opportunitiesToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 5: FIND CONTACTS // ═══════════════════════════════════════════════════════════════════════════════ List contactsToDelete = [ SELECT Id, FirstName, LastName, Email, AccountId FROM Contact WHERE LastName LIKE 'Contact_%' OR LastName LIKE 'Hierarchy%' OR LastName LIKE 'Test%' OR Email LIKE '%@testfactory.example.com' OR Email LIKE '%@bulktest.example.com' OR Email LIKE '%@batchtest.example.com' LIMIT :maxRecordsPerObject ]; recordCounts.put('Contact', contactsToDelete.size()); System.debug('📋 Contacts found: ' + contactsToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 6: FIND LEADS // ═══════════════════════════════════════════════════════════════════════════════ List leadsToDelete = [ SELECT Id, FirstName, LastName, Company FROM Lead WHERE LastName LIKE 'Lead%' OR LastName LIKE 'Test%' OR Company LIKE 'Test Company%' OR Email LIKE '%@testlead.example.com' LIMIT :maxRecordsPerObject ]; recordCounts.put('Lead', leadsToDelete.size()); System.debug('📋 Leads found: ' + leadsToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // STEP 7: FIND ACCOUNTS (delete last - parent records) // ═══════════════════════════════════════════════════════════════════════════════ List accountsToDelete = [ SELECT Id, Name FROM Account WHERE Name LIKE 'Test%' OR Name LIKE 'BulkTest%' OR Name LIKE 'BatchTest%' OR Name LIKE 'BulkData%' OR Name LIKE 'BulkImport%' OR Name LIKE 'Hierarchy%' OR Name LIKE 'Root Account%' OR Name LIKE 'Level%' LIMIT :maxRecordsPerObject ]; recordCounts.put('Account', accountsToDelete.size()); System.debug('📋 Accounts found: ' + accountsToDelete.size()); // ═══════════════════════════════════════════════════════════════════════════════ // SUMMARY // ═══════════════════════════════════════════════════════════════════════════════ Integer totalRecords = 0; for (String objName : recordCounts.keySet()) { totalRecords += recordCounts.get(objName); } System.debug(''); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('CLEANUP SUMMARY'); System.debug('═══════════════════════════════════════════════════════════════'); System.debug(' Tasks: ' + recordCounts.get('Task')); System.debug(' Events: ' + recordCounts.get('Event')); System.debug(' Cases: ' + recordCounts.get('Case')); System.debug(' Opportunities: ' + recordCounts.get('Opportunity')); System.debug(' Contacts: ' + recordCounts.get('Contact')); System.debug(' Leads: ' + recordCounts.get('Lead')); System.debug(' Accounts: ' + recordCounts.get('Account')); System.debug(' ─────────────────────────────────'); System.debug(' TOTAL: ' + totalRecords); System.debug('═══════════════════════════════════════════════════════════════'); // ═══════════════════════════════════════════════════════════════════════════════ // EXECUTE DELETE // ═══════════════════════════════════════════════════════════════════════════════ if (!previewOnly && totalRecords > 0) { System.debug(''); System.debug('⚠️ DELETING RECORDS...'); System.debug(' Deleting in dependency order (children before parents)'); System.debug(''); Integer deletedCount = 0; // Delete in order: Activities → Children → Parents try { if (!tasksToDelete.isEmpty()) { delete tasksToDelete; deletedCount += tasksToDelete.size(); System.debug(' ✓ Deleted ' + tasksToDelete.size() + ' Tasks'); } if (!eventsToDelete.isEmpty()) { delete eventsToDelete; deletedCount += eventsToDelete.size(); System.debug(' ✓ Deleted ' + eventsToDelete.size() + ' Events'); } if (!casesToDelete.isEmpty()) { delete casesToDelete; deletedCount += casesToDelete.size(); System.debug(' ✓ Deleted ' + casesToDelete.size() + ' Cases'); } if (!opportunitiesToDelete.isEmpty()) { delete opportunitiesToDelete; deletedCount += opportunitiesToDelete.size(); System.debug(' ✓ Deleted ' + opportunitiesToDelete.size() + ' Opportunities'); } if (!contactsToDelete.isEmpty()) { delete contactsToDelete; deletedCount += contactsToDelete.size(); System.debug(' ✓ Deleted ' + contactsToDelete.size() + ' Contacts'); } if (!leadsToDelete.isEmpty()) { delete leadsToDelete; deletedCount += leadsToDelete.size(); System.debug(' ✓ Deleted ' + leadsToDelete.size() + ' Leads'); } if (!accountsToDelete.isEmpty()) { delete accountsToDelete; deletedCount += accountsToDelete.size(); System.debug(' ✓ Deleted ' + accountsToDelete.size() + ' Accounts'); } System.debug(''); System.debug('═══════════════════════════════════════════════════════════════'); System.debug('✅ CLEANUP COMPLETE'); System.debug(' Deleted: ' + deletedCount + ' records'); System.debug(' Status: Moved to Recycle Bin'); System.debug('═══════════════════════════════════════════════════════════════'); } catch (DmlException e) { System.debug(''); System.debug('❌ DELETE ERROR: ' + e.getMessage()); System.debug(' Some records may have dependencies preventing deletion.'); System.debug(' Try running again after resolving dependencies.'); } } else if (previewOnly) { System.debug(''); System.debug('ℹ️ PREVIEW MODE - No records deleted'); System.debug(' To delete, set previewOnly = false and run again'); System.debug(''); System.debug(' ⚠️ Please review the counts above before deleting!'); } // ═══════════════════════════════════════════════════════════════════════════════ // BATCH APEX VERSION (For very large datasets) // ═══════════════════════════════════════════════════════════════════════════════ /* For datasets larger than 10,000 records, use batch Apex: public class TestDataCleanupBatch implements Database.Batchable { private String objectName; private String query; public TestDataCleanupBatch(String objName, String soqlQuery) { this.objectName = objName; this.query = soqlQuery; } public Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator(query); } public void execute(Database.BatchableContext bc, List records) { delete records; } public void finish(Database.BatchableContext bc) { System.debug('Cleanup complete for ' + objectName); } } // Execute for each object (in order): Database.executeBatch(new TestDataCleanupBatch('Task', 'SELECT Id FROM Task WHERE Subject LIKE \'Test%\''), 200); Database.executeBatch(new TestDataCleanupBatch('Account', 'SELECT Id FROM Account WHERE Name LIKE \'Test%\''), 200); */