diff --git a/modules/salesforce_mapping/salesforce_mapping.module b/modules/salesforce_mapping/salesforce_mapping.module
index 0aea9ced238387671f11a56b6e9785635ae803ea..d6f5b4216f2204bf27f12e39f260c993816bb7c1 100644
--- a/modules/salesforce_mapping/salesforce_mapping.module
+++ b/modules/salesforce_mapping/salesforce_mapping.module
@@ -43,7 +43,36 @@ function salesforce_mapping_entity_update(EntityInterface $entity) {
 function salesforce_mapping_entity_delete(EntityInterface $entity) {
   $storage = \Drupal::entityTypeManager()
     ->getStorage('salesforce_mapped_object');
-  $storage->delete($storage->loadByEntity($entity));
+
+  if (\Drupal::moduleHandler()->moduleExists('salesforce_push') &&
+    \Drupal::database()->schema()->tableExists('salesforce_push_queue')) {
+    $mapped_objects = $storage->loadByEntity($entity);
+
+    if (!$mapped_objects) {
+      return;
+    }
+
+    foreach ($mapped_objects as $mapped_object) {
+      $mapping = $mapped_object->getMapping();
+      $queued_items = \Drupal::database()
+        ->select('salesforce_push_queue', 'spq')
+        ->fields('spq', ['item_id'])
+        ->condition('entity_id', $entity->id())
+        ->condition('name', $mapping->id())
+        ->condition('op', 'push_delete')
+        ->execute();
+
+      // If this was queued to be deleted, don't delete the mapping yet, defer
+      // to salesforce_push.
+      if (!$queued_items->fetch()) {
+        $mapped_object->delete();
+      }
+    }
+  }
+  else {
+    $storage->delete($storage->loadByEntity($entity));
+  }
+
 }
 
 /**
@@ -53,3 +82,18 @@ function salesforce_mapping_field_formatter_info_alter(array &$info) {
   $info['link']['field_types'][] = 'mapped_entity_link';
   $info['link']['field_types'][] = 'salesforce_link';
 }
+
+/**
+ * Implements hook_module_implements_alter().
+ */
+function salesforce_mapping_module_implements_alter(&$implementations, $hook) {
+  // This needs to go last to make sure the mapping isn't deleted before
+  // everything is done being cleaned up.
+  switch ($hook) {
+    case 'entity_delete':
+      $group = $implementations['salesforce_mapping'];
+      unset($implementations['salesforce_mapping']);
+      $implementations['salesforce_mapping'] = $group;
+      break;
+  }
+}
diff --git a/modules/salesforce_mapping/tests/modules/salesforce_mapping_test/config/install/salesforce_mapping.salesforce_mapping.test_mapping.yml b/modules/salesforce_mapping/tests/modules/salesforce_mapping_test/config/install/salesforce_mapping.salesforce_mapping.test_mapping.yml
index 757b81a4c4b5344f2b79b1ffce6793ad79c47a4e..9bd338e040efce610292a102997801150e7964be 100644
--- a/modules/salesforce_mapping/tests/modules/salesforce_mapping_test/config/install/salesforce_mapping.salesforce_mapping.test_mapping.yml
+++ b/modules/salesforce_mapping/tests/modules/salesforce_mapping_test/config/install/salesforce_mapping.salesforce_mapping.test_mapping.yml
@@ -25,10 +25,10 @@ pull_where_clause: ''
 sync_triggers:
   push_create: true
   push_update: true
-  push_delete: false
+  push_delete: true
   pull_create: true
   pull_update: true
-  pull_delete: false
+  pull_delete: true
 salesforce_object_type: Contact
 drupal_entity_type: node
 drupal_bundle: salesforce_mapping_test_content
diff --git a/modules/salesforce_push/src/Plugin/SalesforcePushQueueProcessor/Rest.php b/modules/salesforce_push/src/Plugin/SalesforcePushQueueProcessor/Rest.php
index 212527b1b80dfb4a5bb3933a6cea3c9865d0497e..ce863b125d7ad84467be71ebe48f7196c48db3f6 100644
--- a/modules/salesforce_push/src/Plugin/SalesforcePushQueueProcessor/Rest.php
+++ b/modules/salesforce_push/src/Plugin/SalesforcePushQueueProcessor/Rest.php
@@ -160,6 +160,9 @@ class Rest extends PluginBase implements PushQueueProcessorInterface {
       // If this is a delete, destroy the SF object and we're done.
       if ($item->op == MappingConstants::SALESFORCE_MAPPING_SYNC_DRUPAL_DELETE) {
         $mapped_object->pushDelete();
+        // This has to be cleaned up here because we need the object to process
+        // Async.
+        $mapped_object->delete();
       }
       else {
         $entity = $this->etm
diff --git a/modules/salesforce_push/tests/src/Functional/PushQueueTest.php b/modules/salesforce_push/tests/src/Functional/PushQueueTest.php
index 9e9096898dacecd3620a8e02ee28b7b0f13ec2ed..e100f01522cefaafed8a5a3aab76dca690271942 100644
--- a/modules/salesforce_push/tests/src/Functional/PushQueueTest.php
+++ b/modules/salesforce_push/tests/src/Functional/PushQueueTest.php
@@ -3,6 +3,8 @@
 namespace Drupal\Tests\salesforce_push\Functional;
 
 use Drupal\node\Entity\Node;
+use Drupal\salesforce_mapping\Entity\MappedObject;
+use Drupal\salesforce_mapping\Entity\SalesforceMapping;
 use Drupal\Tests\BrowserTestBase;
 
 /**
@@ -15,27 +17,98 @@ class PushQueueTest extends BrowserTestBase {
   public static $modules = ['typed_data', 'dynamic_entity_reference', 'salesforce_mapping', 'salesforce_mapping_test', 'salesforce_push'];
 
   /**
-   * Test that saving mapped nodes enqueues them for push to Salesforce.
+   * Test queue features.
+   *
+   * Test creation of queue items.
+   * Test mocked push queue create and creation of mapped objects.
+   * Test mocked push queue update and update of mapped objects.
+   * Test deletion of entities and corresponding deletion of SF related records.
    */
-  public function testEnqueue() {
+  public function testQueue() {
     /** @var \Drupal\salesforce_push\PushQueue $queue */
     $queue = \Drupal::service('queue.salesforce_push');
+    $mapping = SalesforceMapping::load('test_mapping');
+    // Set mapping to async, otherwise the push will be attempted immediately.
+    $mapping->set('async', TRUE);
+    $mapping->save();
+
+    /** @var \Drupal\salesforce_mapping\MappedObjectStorage $mappedObjectStorage */
+    $mappedObjectStorage = \Drupal::entityTypeManager()->getStorage('salesforce_mapped_object');
     $this->assertEquals(0, $queue->numberOfItems());
 
-    Node::create([
+    $node1 = Node::create([
         'type' => 'salesforce_mapping_test_content',
         'title' => 'Test Example',
       ]
-    )->save();
+    );
+    $node1->save();
     $this->assertEquals(1, $queue->numberOfItems());
 
-    Node::create([
+    $node2 = Node::create([
         'type' => 'salesforce_mapping_test_content',
         'title' => 'Test Example 2',
       ]
-    )->save();
+    );
+    $node2->save();
+    $this->assertEquals(2, $queue->numberOfItems());
+
+    // Two items are queued. Run the queue and ensure that mapped objects are
+    // created from our mocked results.
+    $queue->processQueue($mapping);
+    $this->assertEquals(0, $queue->numberOfItems());
+
+    $mappedObject1 = $mappedObjectStorage->loadByEntityAndMapping($node1, $mapping);
+    $this->assertNotNull($mappedObject1);
+    $mappedObject1Vid = $mappedObject1->getRevisionId();
+
+    $mappedObject2 = $mappedObjectStorage->loadByEntityAndMapping($node2, $mapping);
+    $this->assertNotNull($mappedObject2);
+    $mappedObject2Vid = $mappedObject2->getRevisionId();
+
+    // Update those nodes and test that they get re-queued.
+    $node1->setTitle('Updated Title')->save();
+    $this->assertEquals(1, $queue->numberOfItems());
+
+    $node2->setTitle('Updated Title 2')->save();
+    $this->assertEquals(2, $queue->numberOfItems());
+
+    // Run the queue again and test that mapped objects get updated.
+    $queue->processQueue($mapping);
+    $this->assertEquals(0, $queue->numberOfItems());
+
+    // Make sure a new revision was created, implying the update.
+    $mappedObjectStorage->resetCache([$mappedObject1->id(), $mappedObject2->id()]);
+
+    $mappedObject1 = $mappedObjectStorage->loadByEntityAndMapping($node1, $mapping);
+    $mappedObject1UpdatedVid = $mappedObject1->getRevisionId();
+    $this->assertNotEquals($mappedObject1Vid, $mappedObject1UpdatedVid);
+
+    $mappedObject2 = $mappedObjectStorage->loadByEntityAndMapping($node2, $mapping);
+    $mappedObject2UpdatedVid = $mappedObject2->getRevisionId();
+    $this->assertNotEquals($mappedObject2Vid, $mappedObject2UpdatedVid);
+
+    // Delete nodes and test that corresponding records are deleted.
+    $node1->delete();
+    $node2->delete();
+
+    // Make sure the mapped object is not deleted yet.
+    $mappedObjectStorage->resetCache([$mappedObject1->id(), $mappedObject2->id()]);
+    $mappedObject1 = $mappedObjectStorage->load($mappedObject1->id());
+    $this->assertNotNull($mappedObject1);
+    $mappedObject2 = $mappedObjectStorage->load($mappedObject2->id());
+    $this->assertNotNull($mappedObject2);
+
+    // Make sure the delete queue items were created.
     $this->assertEquals(2, $queue->numberOfItems());
 
+    // Process the queue to make sure the deletes get sent.
+    $queue->processQueue($mapping);
+    $this->assertEquals(0, $queue->numberOfItems());
+
+    // Finally, make sure the mapped objects have been deleted.
+    $mappedObjectStorage->resetCache([$mappedObject1->id(), $mappedObject2->id()]);
+    $this->assertNull($mappedObjectStorage->load($mappedObject1->id()));
+    $this->assertNull($mappedObjectStorage->load($mappedObject2->id()));
   }
 
 }
\ No newline at end of file
diff --git a/src/Tests/TestRestClient.php b/src/Tests/TestRestClient.php
index f67922e0f127b00bdb50a7e0fa81159c806df5f9..9ca618cef68043c4c942482f93ab28cbe22de01a 100644
--- a/src/Tests/TestRestClient.php
+++ b/src/Tests/TestRestClient.php
@@ -2,10 +2,12 @@
 
 namespace Drupal\salesforce\Tests;
 
+use Drupal\Component\Utility\Random;
 use Drupal\salesforce\Rest\RestResponse;
 use Drupal\salesforce\Rest\RestResponseDescribe;
 use Drupal\salesforce\SelectQueryInterface;
 use Drupal\salesforce\SelectQueryResult;
+use Drupal\salesforce\SFID;
 use Drupal\salesforce\SObject;
 use GuzzleHttp\Psr7\Response;
 use Drupal\salesforce\Rest\RestClient;
@@ -119,4 +121,37 @@ class TestRestClient extends RestClient {
     return (object) ['resources' => []];
   }
 
+  /**
+   * @inheritDoc
+   */
+  public function objectCreate($name, array $params) {
+    $random = new Random();
+    return new SFID(strtoupper($random->string(SFID::MAX_LENGTH)));
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function objectUpsert($name, $key, $value, array $params) {
+    $random = new Random();
+    return new SFID(strtoupper($random->string(SFID::MAX_LENGTH)));
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function objectUpdate($name, $id, array $params) {
+    // Object update does... NOTHING!
+    return NULL;
+  }
+
+  /**
+   * @inheritDoc
+   */
+  public function objectDelete($name, $id, $throw_exception = FALSE) {
+    // Object delete does... NOTHING!
+    return NULL;
+  }
+
+
 }
diff --git a/src/Tests/TestSalesforceAuthProviderPluginManager.php b/src/Tests/TestSalesforceAuthProviderPluginManager.php
index 83a5dea912c3402b89745e7224beb0c6c69473ba..a3438eddacca15edf7af338aa6d09f6f0417de15 100644
--- a/src/Tests/TestSalesforceAuthProviderPluginManager.php
+++ b/src/Tests/TestSalesforceAuthProviderPluginManager.php
@@ -3,10 +3,15 @@
 namespace Drupal\salesforce\Tests;
 
 use Drupal\salesforce\SalesforceAuthProviderPluginManager;
+use Drupal\salesforce\Token\SalesforceToken;
 
 class TestSalesforceAuthProviderPluginManager extends SalesforceAuthProviderPluginManager {
 
   public function getProvider() {
     return new TestSalesforceAuthProvider();
   }
+
+  public function getToken() {
+    return new SalesforceToken();
+  }
 }
\ No newline at end of file