Setup
Use fresh UAT records so every routing node can be tested without prior ownership, tasks, exceptions, or cadence history changing the evidence.
Default test data for every case unless overridden: run as a user with Routing_Trigger_Users and without Routing_Bypass_All. Contact values: LastName = UAT Router {case id}, Email = uat-router-{case id}@example.com, On_Hold__c = false, Contact_Status__c = Open. Account values: Name = UAT Router {case id}, Account_Status__c = Open, Target_Account_Status__c = Not a Target Account, Do_Not_Route__c = false, OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = blank, Region__c = NAMER-East, Revenue_Range_Segments__c = Velocity ($50M). Do not create open Opportunities unless the test explicitly requires them.
Unless a row says otherwise, create the Contact first with a non-MQL lifecycle stage, then update Contact.lifecyclestage__c to Marketing Qualified Lead. The before-save Flow stamps Contact_Status__c = MQL, and the after-save Flow invokes ContactRouter.
Fixtures
Prepare or identify these UAT users and metadata rows before running the node tests.
| Fixture | Required UAT record | Field values |
|---|---|---|
UAT_POOL_USER |
Prospecting Pool user from Routing_Config__mdt.Default.Prospecting_Pool_User_Id__c. |
User.IsActive = true. Use this exact User as the Account owner when a test says owner is Pool. |
UAT_ACTIVE_AE_EAST |
Active AE used for Account ownership and tagged/customer routing. | User.IsActive = true, not the Prospecting Pool. Roster row: Role__c = AE, Region__c = NAMER-East, Segment__c = Velocity ($50M), Is_Active__c = true, Out_Until__c = blank or a past date. |
UAT_ACTIVE_BDR_EAST |
Active BDR used for Account BDR and rotation tests. | User.IsActive = true. Roster row: Role__c = BDR, Region__c = NAMER-East, Segment__c = blank, Is_Active__c = true, Out_Until__c = blank or a past date. |
UAT_OPP_OWNER |
Active User for open Opportunity routing. | User.IsActive = true, not the Prospecting Pool. This user does not need a roster row because ACTIVE_DEAL reads Opportunity ownership. |
UAT_ACTIVE_AE_EMEA |
Active AE used only for no-BDR negative tests. | User.IsActive = true, not the Prospecting Pool. Roster row: Role__c = AE, Region__c = EMEA, Segment__c = Velocity ($50M), Is_Active__c = true, Out_Until__c = blank or a past date. Confirm there is no active BDR roster row for Region__c = EMEA while running UAT-20 and UAT-21. |
UAT_INACTIVE_USER |
Inactive User for broken owner and stale BDR tests. | User.IsActive = false. Use an existing inactive test user or deactivate a throwaway UAT user before running these cases. |
UAT_TRIAGE_OWNER |
Queue or user configured for routing exceptions. | Routing_Config__mdt.Default.Unrouted_Triage_Owner_Id__c points to this owner. Expected exception records should be owned here. |
Node Tests
Each row targets a specific gate, first-match branch, fall-through condition, routed path, or unrouted outcome.
| Test | Node or outcome | Field values to set | Expected result |
|---|---|---|---|
| UAT-00 | Trigger permission gate exits | Run as a user without Routing_Trigger_Users. Use the default Contact and Account values, then set lifecyclestage__c = Marketing Qualified Lead. |
No routing: Contact.MQL_Stamped_At__c remains blank, Contact.Contact_Status__c remains unchanged, Contact.Routing_Path__c remains blank, and ownership remains unchanged. |
| UAT-01 | Bypass gate exits | Run as a user with both Routing_Trigger_Users and Routing_Bypass_All. Use default Contact and Account values, then set lifecyclestage__c = Marketing Qualified Lead. |
No routing: MQL_Stamped_At__c, Contact_Status__c, and Routing_Path__c remain unchanged. Remove Routing_Bypass_All before continuing UAT. |
| UAT-02 | HOLD from Contact hold |
Default Account values. Contact override: On_Hold__c = true. |
Routing_Path__c = HOLD, Routing_Last_Run__c populated, Contact owner unchanged, no Routing_Exception__c, no cadence, no platform event. |
| UAT-03 | HOLD from Account do-not-route |
Account override: Do_Not_Route__c = true. Contact values remain default. |
Routing_Path__c = HOLD, Contact owner unchanged, no Routing_Exception__c, no cadence, no platform event. |
| UAT-04 | HOLD from disqualified Contact |
Use default Account values. Contact override: Contact_Status__c = Disqualified, lifecyclestage__c can stay non-MQL. Invoke ContactRouter.route directly with the Contact Id from Execute Anonymous because the normal MQL Flow stamps Contact_Status__c = MQL before routing. |
Routing_Path__c = HOLD, Contact owner unchanged, no Routing_Exception__c. This verifies the Apex gate even though the current MQL trigger path normally overwrites the status to MQL. |
| UAT-05 | UNROUTED_BAD_DATA from blank Email |
Default Account values. Contact override: Email = blank, AccountId = test Account Id. |
Routing_Path__c = UNROUTED_BAD_DATA, Contact owner unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_BAD_DATA and Error_Detail__c containing Contact.Email is blank. |
| UAT-06 | UNROUTED_BAD_DATA from missing Account |
Create a Contact with no Account. Contact values: Email = uat-router-UAT-06@example.com, AccountId = blank, On_Hold__c = false, Contact_Status__c = Open. |
Routing_Path__c = UNROUTED_BAD_DATA, Contact owner unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_BAD_DATA and Error_Detail__c containing Contact.AccountId is null. |
| UAT-07 | TAGGED_ACCOUNT Active Target |
Account overrides: Target_Account_Status__c = Active Target Account, OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = UAT_ACTIVE_BDR_EAST, Account_Status__c = Open. |
Contact owner becomes UAT_ACTIVE_AE_EAST, Routing_Path__c = TAGGED_ACCOUNT, Contact_Status__c = Working, one manual outreach Task is created for UAT_ACTIVE_BDR_EAST, and no cadence enrollment occurs. |
| UAT-08 | TAGGED_ACCOUNT Conversion and Velocity variants |
Run twice with the same Account setup as UAT-07 except set Target_Account_Status__c = Active Conversion Account in one run and Target_Account_Status__c = Active Velocity Account in the other. |
Both Contacts route to UAT_ACTIVE_AE_EAST with Routing_Path__c = TAGGED_ACCOUNT. No manual outreach Task is created for either variant, and no cadence enrollment occurs. |
| UAT-09 | UNROUTED_TAGGED_NO_OWNER |
Account overrides: Target_Account_Status__c = Active Target Account, OwnerId = UAT_POOL_USER, BDR_Owner__c = UAT_ACTIVE_BDR_EAST, Account_Status__c = Open. |
Routing_Path__c = UNROUTED_TAGGED_NO_OWNER, Contact owner unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_TAGGED_NO_OWNER. No fall-through to BDR or rotation. |
| UAT-10 | CUSTOMER_ACCOUNT |
Account overrides: Account_Status__c = Customer, Target_Account_Status__c = Not a Target Account, OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = blank. |
Contact owner becomes UAT_ACTIVE_AE_EAST, Routing_Path__c = CUSTOMER_ACCOUNT, no Task, no cadence, no exception. |
| UAT-11 | UNROUTED_NO_OWNER for Customer Account |
Account overrides: Account_Status__c = Customer, OwnerId = UAT_POOL_USER, BDR_Owner__c = UAT_ACTIVE_BDR_EAST. Optionally add an open Opportunity owned by UAT_OPP_OWNER to prove customer broken ownership does not fall through. |
Routing_Path__c = UNROUTED_NO_OWNER, Contact owner unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_OWNER. It must not route to the BDR or Opportunity owner. |
| UAT-12 | ACTIVE_DEAL |
Default Account values with BDR_Owner__c = blank. Create two open Opportunities on the Account: older Opp StageName = Prospecting, CloseDate = today + 30, OwnerId = UAT_ACTIVE_AE_EAST; newer Opp StageName = Qualification, CloseDate = today + 45, OwnerId = UAT_OPP_OWNER. Create the newer Opp second. |
Contact owner becomes the newer open Opportunity owner UAT_OPP_OWNER, Routing_Path__c = ACTIVE_DEAL, no Task, no cadence, no exception. |
| UAT-13 | Open deal fall-through when Opp owner is Pool | Account overrides: BDR_Owner__c = UAT_ACTIVE_BDR_EAST. Create one open Opportunity with StageName = Prospecting, CloseDate = today + 30, OwnerId = UAT_POOL_USER. |
The Contact does not use ACTIVE_DEAL. It falls through to ACCOUNT_BDR, Contact owner becomes UAT_ACTIVE_BDR_EAST, and the BDR cadence branch is eligible. |
| UAT-14 | ACCOUNT_BDR |
Default Account values with BDR_Owner__c = UAT_ACTIVE_BDR_EAST. No open Opportunities. |
Contact owner becomes UAT_ACTIVE_BDR_EAST, Routing_Path__c = ACCOUNT_BDR, no Task, no exception. If NonTarget_Cadence_Id__c is populated, the Contact is enrolled in the non-Target cadence. |
| UAT-15 | Inactive BDR pointer falls through and self-heals | Account overrides: OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = UAT_INACTIVE_USER, Region__c = NAMER-East, Revenue_Range_Segments__c = Velocity ($50M). Ensure the active East BDR roster row exists. |
The Contact does not use ACCOUNT_BDR. It routes via ROTATION_BDR_ONLY, Contact owner becomes the rotated active East BDR, and Account.BDR_Owner__c is overwritten with that active BDR. |
| UAT-16 | ROTATION_NEW_ACCOUNT |
Account overrides: OwnerId = UAT_POOL_USER, BDR_Owner__c = blank, Region__c = NAMER-East, Revenue_Range_Segments__c = Velocity ($50M). Ensure eligible AE and BDR roster rows exist for those values. |
Account owner becomes the rotated AE, Contact owner becomes the rotated BDR, Account.BDR_Owner__c equals the Contact owner, Routing_Path__c = ROTATION_NEW_ACCOUNT, and the BDR cadence branch is eligible. |
| UAT-17 | UNROUTED_NO_REGION on new-account rotation branch |
Account overrides: OwnerId = UAT_POOL_USER, BDR_Owner__c = blank, Region__c = blank, Revenue_Range_Segments__c = Velocity ($50M). |
Routing_Path__c = UNROUTED_NO_REGION, ownership unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_REGION. No rotation counter should advance. |
| UAT-18 | UNROUTED_NO_REGION on BDR-only branch |
Account overrides: OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = blank, Region__c = blank, Revenue_Range_Segments__c = Velocity ($50M). |
Routing_Path__c = UNROUTED_NO_REGION, ownership unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_REGION. |
| UAT-19 | UNROUTED_NO_AE |
Use a region with an eligible BDR but no eligible AE for the exact Account segment. Seeded example: Account overrides OwnerId = UAT_POOL_USER, BDR_Owner__c = blank, Region__c = NAMER-East, Revenue_Range_Segments__c = Strategic ($500M+), with no active Rep_Roster__mdt row where Role__c = AE, Region__c = NAMER-East, and Segment__c = Strategic ($500M+). |
Routing_Path__c = UNROUTED_NO_AE, ownership unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_AE. The BDR counter must not advance. |
| UAT-20 | UNROUTED_NO_BDR on new-account rotation branch |
Account overrides: OwnerId = UAT_POOL_USER, BDR_Owner__c = blank, Region__c = EMEA, Revenue_Range_Segments__c = Velocity ($50M). Confirm UAT_ACTIVE_AE_EMEA exists and no active BDR roster row exists for Role__c = BDR, Region__c = EMEA. |
Routing_Path__c = UNROUTED_NO_BDR, ownership unchanged, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_BDR. The AE counter must not advance. |
| UAT-21 | UNROUTED_NO_BDR on BDR-only branch |
Account overrides: OwnerId = UAT_ACTIVE_AE_EMEA, BDR_Owner__c = blank, Region__c = EMEA, Revenue_Range_Segments__c = Velocity ($50M). Confirm no active BDR roster row exists for Role__c = BDR, Region__c = EMEA. |
Routing_Path__c = UNROUTED_NO_BDR, Contact owner unchanged, Account owner preserved, one Routing_Exception__c with Failure_Reason__c = UNROUTED_NO_BDR. |
| UAT-22 | ROTATION_BDR_ONLY |
Account overrides: OwnerId = UAT_ACTIVE_AE_EAST, BDR_Owner__c = blank, Region__c = NAMER-East, Revenue_Range_Segments__c = Velocity ($50M). Ensure the eligible East BDR roster row exists. |
Account owner remains UAT_ACTIVE_AE_EAST, Contact owner becomes the rotated East BDR, Account.BDR_Owner__c equals the Contact owner, Routing_Path__c = ROTATION_BDR_ONLY, and the BDR cadence branch is eligible. |
Cross Checks
Run these after the node tests to verify batch behavior, cadence branching, triage ownership, event publishing, and roster eligibility.
| Test | Behavior covered | How to run | Expected result |
|---|---|---|---|
| UAT-23 | Buying-group continuity | Create one Account that would route through ROTATION_NEW_ACCOUNT: OwnerId = UAT_POOL_USER, Region__c = NAMER-East, Revenue_Range_Segments__c = Velocity ($50M), BDR_Owner__c = blank. Create two Contacts on that same Account with unique Emails, then bulk update both Contacts to lifecyclestage__c = Marketing Qualified Lead in one Data Loader or list-view transaction. |
Both Contacts get Routing_Path__c = ROTATION_NEW_ACCOUNT and the same BDR owner. The Account gets one AE owner and one BDR_Owner__c value, not two separate rotations for the same Account in the same transaction. |
| UAT-24 | Cadence branch only for BDR-routed paths | Run UAT-10, UAT-12, UAT-14, UAT-16, and UAT-22 with Routing_Config__mdt.Default.NonTarget_Cadence_Id__c populated. |
Only ACCOUNT_BDR, ROTATION_NEW_ACCOUNT, and ROTATION_BDR_ONLY Contacts are enrolled in the non-Target cadence. CUSTOMER_ACCOUNT and ACTIVE_DEAL are not enrolled. |
| UAT-25 | Routing exceptions queue ownership | Run UAT-05, UAT-09, UAT-11, UAT-17, UAT-19, UAT-20, and UAT-21. | Each failed route creates exactly one Routing_Exception__c with Status__c = Open, the expected Failure_Reason__c, a populated Routed_At__c, and OwnerId = UAT_TRIAGE_OWNER. |
| UAT-26 | Platform event publication | Run one successful AE-routed case and one successful BDR-routed case, such as UAT-10 and UAT-16. Monitor Contact_Routed__e delivery or the subscribed downstream process if that integration is in UAT scope. |
Successful routed Contacts publish an event with Contact_Id__c, Routing_Path__c, Routing_Timestamp__c, Account_Owner__c, and Region__c. UNROUTED_* and HOLD outcomes do not publish events. |
| UAT-27 | Roster out-of-office exclusion | Temporarily set the only BDR roster row for a controlled region to Out_Until__c = tomorrow, or set Is_Active__c = false. Run a BDR-needed route for that region, then restore the roster row. |
The out-of-office or inactive rep is skipped. If no other BDR is eligible, the Contact lands on UNROUTED_NO_BDR; if another BDR is eligible, routing selects the other BDR. |
Cleanup
Restore UAT configuration and remove test evidence after sign-off.
UAT cleanup: restore any permission assignments, roster rows, and cadence configuration changed for negative tests. Close or delete test Opportunities, Contacts, Accounts, Tasks, and open Routing_Exception__c records after evidence is captured.