mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-24 10:11:08 +02:00
Add test for race condition
This commit is contained in:
parent
511e9610e2
commit
1ece895ac6
2 changed files with 64 additions and 13 deletions
|
@ -1939,6 +1939,8 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
Additionally, capture and cache old hosts and contacts from cache if they
|
Additionally, capture and cache old hosts and contacts from cache if they
|
||||||
don't exist in cleaned
|
don't exist in cleaned
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# object reference issue between self._cache vs cleaned?
|
||||||
old_cache_hosts = self._cache.get("hosts")
|
old_cache_hosts = self._cache.get("hosts")
|
||||||
old_cache_contacts = self._cache.get("contacts")
|
old_cache_contacts = self._cache.get("contacts")
|
||||||
|
|
||||||
|
@ -2111,19 +2113,20 @@ class Domain(TimeStampedModel, DomainHelper):
|
||||||
# Save to DB if it doesn't exist already.
|
# Save to DB if it doesn't exist already.
|
||||||
if db_contact.count() == 0:
|
if db_contact.count() == 0:
|
||||||
# Doesn't run custom save logic, just saves to DB
|
# Doesn't run custom save logic, just saves to DB
|
||||||
try:
|
|
||||||
public_contact.save(skip_epp_save=True)
|
public_contact.save(skip_epp_save=True)
|
||||||
logger.info(f"Created a new PublicContact: {public_contact}")
|
# try:
|
||||||
except IntegrityError as err:
|
# public_contact.save(skip_epp_save=True)
|
||||||
logger.error(
|
# logger.info(f"Created a new PublicContact: {public_contact}")
|
||||||
"_get_or_create_public_contact() => tried to create a duplicate public contact: "
|
# except IntegrityError as err:
|
||||||
f"{err}", exc_info=True
|
# logger.error(
|
||||||
)
|
# "_get_or_create_public_contact() => tried to create a duplicate public contact: "
|
||||||
return PublicContact.objects.get(
|
# f"{err}", exc_info=True
|
||||||
registry_id=public_contact.registry_id,
|
# )
|
||||||
contact_type=public_contact.contact_type,
|
# return PublicContact.objects.get(
|
||||||
domain=self,
|
# registry_id=public_contact.registry_id,
|
||||||
)
|
# contact_type=public_contact.contact_type,
|
||||||
|
# domain=self,
|
||||||
|
# )
|
||||||
|
|
||||||
# Append the item we just created
|
# Append the item we just created
|
||||||
return public_contact
|
return public_contact
|
||||||
|
|
|
@ -348,6 +348,54 @@ class TestDomainCache(MockEppLib):
|
||||||
|
|
||||||
class TestDomainCreation(MockEppLib):
|
class TestDomainCreation(MockEppLib):
|
||||||
"""Rule: An approved domain request must result in a domain"""
|
"""Rule: An approved domain request must result in a domain"""
|
||||||
|
def test_get_security_email_during_unknown_state_race_condition(self):
|
||||||
|
"""
|
||||||
|
Scenario: A domain is accessed for the first time
|
||||||
|
Given a domain in UNKNOWN state with a security contact in registry
|
||||||
|
When get_security_email is called during state transition
|
||||||
|
Then the security contact is fetched from registry
|
||||||
|
And only one security contact exists in database
|
||||||
|
And the security email matches the registry contact
|
||||||
|
And no duplicate contact creation is attempted
|
||||||
|
"""
|
||||||
|
domain, _ = Domain.objects.get_or_create(name="defaultsecurity.gov")
|
||||||
|
|
||||||
|
# Store original method
|
||||||
|
original_filter = PublicContact.objects.filter
|
||||||
|
|
||||||
|
def mock_filter(*args, **kwargs):
|
||||||
|
# First call returns empty queryset to simulate contact not existing
|
||||||
|
result = original_filter(*args, **kwargs)
|
||||||
|
if kwargs.get('contact_type') == PublicContact.ContactTypeChoices.SECURITY:
|
||||||
|
# Create the duplicate contact after the check but before the save
|
||||||
|
duplicate = PublicContact(
|
||||||
|
domain=domain,
|
||||||
|
contact_type=PublicContact.ContactTypeChoices.SECURITY,
|
||||||
|
registry_id="defaultSec",
|
||||||
|
email="dotgov@cisa.dhs.gov",
|
||||||
|
name="Registry Customer Service"
|
||||||
|
)
|
||||||
|
duplicate.save(skip_epp_save=True)
|
||||||
|
return result
|
||||||
|
|
||||||
|
with patch.object(PublicContact.objects, 'filter', side_effect=mock_filter):
|
||||||
|
try:
|
||||||
|
security_email = domain.get_security_email()
|
||||||
|
except IntegrityError:
|
||||||
|
self.fail(
|
||||||
|
"IntegrityError was raised during contact creation due to a race condition. "
|
||||||
|
"This indicates that concurrent contact creation is not working in some cases. "
|
||||||
|
"The error occurs when two processes try to create the same contact simultaneously. "
|
||||||
|
"Expected behavior: gracefully handle duplicate creation and return existing contact."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Verify only one contact exists
|
||||||
|
security_contacts = PublicContact.objects.filter(
|
||||||
|
domain=domain,
|
||||||
|
contact_type=PublicContact.ContactTypeChoices.SECURITY
|
||||||
|
)
|
||||||
|
self.assertEqual(security_contacts.count(), 1)
|
||||||
|
self.assertEqual(security_email, "dotgov@cisa.dhs.gov")
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_approved_domain_request_creates_domain_locally(self):
|
def test_approved_domain_request_creates_domain_locally(self):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue