Repository: getmoto/moto

Issue:
route53.change_resource_record_sets() for DELETE behaves different from real service
Hi,

I believe I have found a bug in Moto concerning Route53: the mocked call behaves differently from the real Route53 service.

Step to reproduce:

```python
import boto3
import moto

mock = moto.mock_route53()
mock.start()

route53_client = boto3.client('route53')
hosted_zone = route53_client.create_hosted_zone(
    Name="github.com",  # I'd like to use example.org, but Route53 refuses that
    CallerReference="whatever",
)
hosted_zone_id = hosted_zone['HostedZone']['Id']

route53_client.change_resource_record_sets(
    HostedZoneId=hosted_zone_id,
    ChangeBatch={'Changes': [
        {
            'Action': 'CREATE',
            'ResourceRecordSet': {
                'Name': 'www.github.com',
                'Type': 'A',
                'TTL': 5,
                'ResourceRecords': [
                    {'Value': "127.0.0.1"},
                ],
            },
        },
        {
            'Action': 'CREATE',
            'ResourceRecordSet': {
                'Name': 'www.github.com',
                'Type': 'TXT',
                'TTL': 5,
                'ResourceRecords': [
                    {'Value': '"foobar"'},
                ],
            },
        },
    ]},
)

route53_client.change_resource_record_sets(
    HostedZoneId=hosted_zone_id,
    ChangeBatch={'Changes': [
        {
            'Action': 'DELETE',
            'ResourceRecordSet': {
                'Name': 'www.github.com',
                'Type': 'A',
                'TTL': 5,
                'ResourceRecords': [
                    {'Value': "127.0.0.1"},
                ],
            },
        },
    ]},
)

records = route53_client.list_resource_record_sets(
    HostedZoneId=hosted_zone_id,
    StartRecordName="www.github.com.",
)

print("\n".join([f"{_['Name']} {_['Type']}" for _ in records['ResourceRecordSets']]))

mock.stop()
```

When running this code mocked, it returns nothing. I.e. it deletes *all* resource records at the given name, irrespective of the given type and/or value.

```
```

The expected result (and the actual result when running against the actual Route53) is:

```
www.github.com. TXT
```


Discussion:

Resolving patch:
diff --git a/moto/route53/models.py b/moto/route53/models.py
--- a/moto/route53/models.py
+++ b/moto/route53/models.py
@@ -119,7 +119,7 @@ def delete_from_cloudformation_json(cls, resource_name, cloudformation_json, reg
                 properties["HostedZoneId"])
 
         try:
-            hosted_zone.delete_rrset_by_name(resource_name)
+            hosted_zone.delete_rrset({'Name': resource_name})
         except KeyError:
             pass
 
@@ -162,7 +162,7 @@ def delete(self, *args, **kwargs):
             self.hosted_zone_name)
         if not hosted_zone:
             hosted_zone = route53_backend.get_hosted_zone(self.hosted_zone_id)
-        hosted_zone.delete_rrset_by_name(self.name)
+        hosted_zone.delete_rrset({'Name': self.name, 'Type': self.type_})
 
 
 class FakeZone(BaseModel):
@@ -190,9 +190,13 @@ def upsert_rrset(self, record_set):
             self.rrsets.append(new_rrset)
         return new_rrset
 
-    def delete_rrset_by_name(self, name):
+    def delete_rrset(self, rrset):
         self.rrsets = [
-            record_set for record_set in self.rrsets if record_set.name != name]
+            record_set
+            for record_set in self.rrsets
+            if record_set.name != rrset['Name'] or
+            (rrset.get('Type') is not None and record_set.type_ != rrset['Type'])
+        ]
 
     def delete_rrset_by_id(self, set_identifier):
         self.rrsets = [
diff --git a/moto/route53/responses.py b/moto/route53/responses.py
--- a/moto/route53/responses.py
+++ b/moto/route53/responses.py
@@ -147,7 +147,7 @@ def rrset_response(self, request, full_url, headers):
                         the_zone.delete_rrset_by_id(
                             record_set["SetIdentifier"])
                     else:
-                        the_zone.delete_rrset_by_name(record_set["Name"])
+                        the_zone.delete_rrset(record_set)
 
             return 200, headers, CHANGE_RRSET_RESPONSE
 


Test patch:
diff --git a/tests/test_route53/test_route53.py b/tests/test_route53/test_route53.py
--- a/tests/test_route53/test_route53.py
+++ b/tests/test_route53/test_route53.py
@@ -110,6 +110,7 @@ def test_rrset():
 
     changes = ResourceRecordSets(conn, zoneid)
     changes.add_change("DELETE", "foo.bar.testdns.aws.com", "A")
+    changes.add_change("DELETE", "foo.bar.testdns.aws.com", "TXT")
     changes.commit()
 
     changes = ResourceRecordSets(conn, zoneid)
@@ -582,7 +583,7 @@ def test_change_resource_record_sets_crud_valid():
     cname_record_detail['TTL'].should.equal(60)
     cname_record_detail['ResourceRecords'].should.equal([{'Value': '192.168.1.1'}])
 
-    # Delete record.
+    # Delete record with wrong type.
     delete_payload = {
         'Comment': 'delete prod.redis.db',
         'Changes': [
@@ -597,6 +598,23 @@ def test_change_resource_record_sets_crud_valid():
     }
     conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=delete_payload)
     response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id)
+    len(response['ResourceRecordSets']).should.equal(1)
+
+    # Delete record.
+    delete_payload = {
+        'Comment': 'delete prod.redis.db',
+        'Changes': [
+            {
+                'Action': 'DELETE',
+                'ResourceRecordSet': {
+                    'Name': 'prod.redis.db',
+                    'Type': 'A',
+                }
+            }
+        ]
+    }
+    conn.change_resource_record_sets(HostedZoneId=hosted_zone_id, ChangeBatch=delete_payload)
+    response = conn.list_resource_record_sets(HostedZoneId=hosted_zone_id)
     len(response['ResourceRecordSets']).should.equal(0)