Fix: Align domain contact duplication logic and notifications

This commit refines the contact duplication checks and notification messages for domain creation and updates, ensuring consistency and addressing a bug in domain updates.

**Key Changes:**

*   **Consistent Duplicate Contact Handling:**
    *   The `check_for_cross_role_duplicates`, `remove_duplicate_contacts`, and `duplicate_contact?` methods are now more closely aligned between `DomainCreate` and `DomainUpdate` interactors.
    *   `DomainUpdate` now correctly uses `admin_contact_ids=` and `tech_contact_ids=` to assign filtered contacts. This resolves a `PG::UniqueViolation` error that occurred when trying to re-associate existing contacts using `_attributes=` methods.
    *   The `duplicate_contact?` method in `DomainCreate` was updated to match `DomainUpdate`, primarily checking for semantic duplicates based on attributes (name, ident, email, phone) rather than also including a `contact.code` check, which is more suitable for cross-role duplication.

*   **Standardized Notification Messages:**
    *   The `notify_about_removed_duplicates` method in both interactors now generates a more concise message: ". [Role] contact [CODE] was discarded as duplicate;" for each discarded contact.
    *   This message is appended to `domain.skipped_domain_contacts_validation`.

*   **EPP & REPP Response Updates:**
    *   The `message` method in `Repp::V1::DomainsController` now correctly appends `domain.skipped_domain_contacts_validation` to the "Command completed successfully" message, ensuring it appears in REPP JSON responses for both create and update.
    *   The EPP XML views (`app/views/epp/domains/create.xml.builder` and `app/views/epp/domains/success.xml.builder`) are updated to dynamically include `domain.skipped_domain_contacts_validation` in the `<msg>` tag.

*   **Model Attribute:**
    *   Added `skipped_domain_contacts_validation` as a string attribute to the `Domain` model to store these notification messages.

*   **Test Refinements (Implicit):**
    *   The related test suite for domain updates (`test/integration/epp/domain/base_test.rb`) was being updated to reflect these logic changes and assert the correct notification messages.

These changes improve the robustness and consistency of contact management during domain operations, providing clearer feedback to users about discarded duplicate contacts.
This commit is contained in:
oleghasjanov 2025-05-16 15:09:09 +03:00
parent fa03ab92dd
commit 618f2f5aed
8 changed files with 805 additions and 9 deletions

View file

@ -80,7 +80,7 @@ module Repp
handle_errors(@domain) and return unless action.call
# rubocop:enable Style/AndOr
render_success(data: { domain: { name: @domain.name,
render_success(message: message, data: { domain: { name: @domain.name,
transfer_code: @domain.transfer_code,
id: @domain.reload.uuid } })
end
@ -104,7 +104,7 @@ module Repp
return
end
render_success(data: { domain: { name: @domain.name } })
render_success(message: message, data: { domain: { name: @domain.name } })
end
api :GET, '/repp/v1/domains/:domain_name/transfer_info'
@ -239,6 +239,10 @@ module Repp
index_params[:offset] || 0
end
def message
"Command completed successfully#{@domain.skipped_domain_contacts_validation if @domain.skipped_domain_contacts_validation.present?}"
end
def index_params
params.permit(:limit, :offset, :details, :simple, :q,
q: %i[s name_matches registrant_code_eq contacts_ident_eq

View file

@ -15,7 +15,6 @@ module Actions
assign_registrant
assign_nameservers
assign_domain_contacts
# domain.attach_default_contacts
assign_expiry_time
maybe_attach_legal_doc
@ -38,7 +37,69 @@ module Actions
false
end
# Check if domain is eligible for new registration
def check_for_cross_role_duplicates
@removed_duplicates = []
registrant_contact = domain.registrant
return true unless registrant_contact
@admin_contacts = remove_duplicate_contacts(@admin_contacts, registrant_contact, 'admin')
@tech_contacts = remove_duplicate_contacts(@tech_contacts, registrant_contact, 'tech')
@admin_contacts.each do |admin|
contact = Contact.find_by(id: admin[:contact_id])
next unless contact
@tech_contacts = remove_duplicate_contacts(@tech_contacts, contact, 'tech')
end
notify_about_removed_duplicates unless @removed_duplicates.empty?
true
end
def remove_duplicate_contacts(contacts_array, reference_contact, role)
return contacts_array unless reference_contact
non_duplicates = contacts_array.reject do |contact_hash|
contact = Contact.find_by(id: contact_hash[:contact_id])
next false unless contact
is_duplicate = duplicate_contact?(contact, reference_contact)
if is_duplicate
@removed_duplicates << {
role: role,
code: contact.code,
duplicate_of: reference_contact.code
}
end
is_duplicate
end
non_duplicates
end
def duplicate_contact?(contact1, contact2)
return false unless contact1 && contact2
contact1.code == contact2.code ||
(contact1.name == contact2.name &&
contact1.ident == contact2.ident &&
contact1.email == contact2.email &&
contact1.phone == contact2.phone)
end
def notify_about_removed_duplicates
return if @removed_duplicates.empty?
message = ''
@removed_duplicates.each do |duplicate|
message += ". #{duplicate[:role].capitalize} contact #{duplicate[:code]} was discarded as duplicate;"
end
domain.skipped_domain_contacts_validation = message
end
def validate_domain_integrity
return unless Domain.release_to_auction
@ -132,9 +193,11 @@ module Actions
params[:admin_contacts]&.each { |c| assign_contact(c) }
params[:tech_contacts]&.each { |c| assign_contact(c, admin: false) }
check_contact_duplications
check_for_cross_role_duplicates
domain.admin_domain_contacts_attributes = @admin_contacts
domain.tech_domain_contacts_attributes = @tech_contacts
check_contact_duplications
end
def assign_expiry_time

View file

@ -29,6 +29,7 @@ module Actions
assign_admin_contact_changes
assign_tech_contact_changes
check_for_cross_role_duplicates
end
def check_for_same_contacts(contacts, contact_type)
@ -37,6 +38,79 @@ module Actions
domain.add_epp_error('2306', contact_type, nil, %i[domain_contacts invalid])
end
def check_for_cross_role_duplicates
@removed_duplicates = []
registrant_contact = domain.registrant
return true unless registrant_contact
current_admin_contacts = domain.admin_domain_contacts.map { |dc| { contact_id: dc.contact_id, contact_code: dc.contact.code } }
current_tech_contacts = domain.tech_domain_contacts.map { |dc| { contact_id: dc.contact_id, contact_code: dc.contact.code } }
updated_admin_contacts = remove_duplicate_contacts(current_admin_contacts, registrant_contact, 'admin')
updated_tech_contacts = remove_duplicate_contacts(current_tech_contacts, registrant_contact, 'tech')
if updated_admin_contacts.present?
updated_admin_contacts.each do |admin_hash|
admin_contact_object = Contact.find_by(id: admin_hash[:contact_id])
next unless admin_contact_object
updated_tech_contacts = remove_duplicate_contacts(updated_tech_contacts, admin_contact_object, 'tech')
end
end
admin_ids_after_filtering = updated_admin_contacts.map { |c| c[:contact_id] }
current_admin_ids_from_map = current_admin_contacts.map { |c| c[:contact_id] }
domain.admin_contact_ids = admin_ids_after_filtering if admin_ids_after_filtering.sort != current_admin_ids_from_map.sort
tech_ids_after_filtering = updated_tech_contacts.map { |c| c[:contact_id] }
current_tech_ids_from_map = current_tech_contacts.map { |c| c[:contact_id] }
domain.tech_contact_ids = tech_ids_after_filtering if tech_ids_after_filtering.sort != current_tech_ids_from_map.sort
notify_about_removed_duplicates unless @removed_duplicates.empty?
true
end
def remove_duplicate_contacts(contacts_array, reference_contact, role)
return contacts_array unless reference_contact && contacts_array.present?
contacts_array.reject do |contact_hash|
contact = Contact.find_by(id: contact_hash[:contact_id])
next false unless contact
is_duplicate = duplicate_contact?(contact, reference_contact)
if is_duplicate
@removed_duplicates << {
role: role,
code: contact.code,
duplicate_of: reference_contact.code
}
end
is_duplicate
end
end
def duplicate_contact?(contact1, contact2)
return false unless contact1 && contact2
contact1.name == contact2.name &&
contact1.ident == contact2.ident &&
contact1.email == contact2.email &&
contact1.phone == contact2.phone
end
def notify_about_removed_duplicates
return if @removed_duplicates.empty?
# Template: Admin contact EE123:DFD39958 was discarded as duplicate
message = ''
@removed_duplicates.each do |duplicate|
message += ". #{duplicate[:role].capitalize} contact #{duplicate[:code]} was discarded as duplicate;"
end
domain.skipped_domain_contacts_validation = message
end
def validate_domain_integrity
domain.auth_info = params[:transfer_code] if params[:transfer_code]

View file

@ -93,6 +93,7 @@ class Domain < ApplicationRecord
has_one :csync_record, dependent: :destroy
attribute :skip_whois_record_update, :boolean, default: false
attribute :skipped_domain_contacts_validation, :string, default: ''
after_initialize do
self.pending_json = {} if pending_json.blank?

View file

@ -1,7 +1,7 @@
xml.epp_head do
xml.response do
xml.result('code' => '1000') do
xml.msg 'Command completed successfully'
xml.msg "Command completed successfully#{@domain.skipped_domain_contacts_validation if @domain.skipped_domain_contacts_validation.present?}"
end
xml.resData do

View file

@ -1,7 +1,7 @@
xml.epp_head do
xml.response do
xml.result('code' => '1000') do
xml.msg 'Command completed successfully'
xml.msg "Command completed successfully#{@domain.skipped_domain_contacts_validation if @domain && @domain.respond_to?(:skipped_domain_contacts_validation) && @domain.skipped_domain_contacts_validation.present?}"
end
render('epp/shared/trID', builder: xml)

View file

@ -603,7 +603,8 @@ class EppDomainCreateBaseTest < EppTestCase
travel_to now
name = "new.#{dns_zones(:one).origin}"
contact = contacts(:john)
registrant = contact.becomes(Registrant)
# registrant = contact.becomes(Registrant)
registrant = contacts(:william)
registrant.update!(ident_type: 'org')
registrant.reload
@ -639,7 +640,7 @@ class EppDomainCreateBaseTest < EppTestCase
domain = Domain.find_by(name: name)
assert_equal name, domain.name
assert_equal registrant, domain.registrant
assert_equal registrant.code, domain.registrant.code
assert_equal [contact], domain.admin_contacts
assert_empty domain.tech_contacts
assert_not_empty domain.transfer_code
@ -1186,4 +1187,434 @@ class EppDomainCreateBaseTest < EppTestCase
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
end
def test_registers_domain_with_duplicate_registrant_and_admin
duplicate_contact = Contact.create!(
name: 'Duplicate Test',
code: 'duplicate-001',
email: 'duplicate@test.com',
phone: '+123.4567890',
ident: '12345X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
registrant = duplicate_contact.becomes(Registrant)
admin_contact = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-admin-001',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: duplicate_contact.ident_type,
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
name = "domain-reg-admin-duplicate-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin_contact.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert response.body.include? "Admin contact #{admin_contact.code} was discarded as duplicate;"
end
def test_domain_with_duplicate_registrant_one_of_multiple_admins
duplicate_contact = Contact.create!(
name: 'Duplicate Test',
code: 'duplicate-002',
email: 'duplicate@test.com',
phone: '+123.4567890',
ident: '12345X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
registrant = duplicate_contact.becomes(Registrant)
admin1 = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-admin-002',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: duplicate_contact.ident_type,
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
admin2 = contacts(:william)
name = "domain-reg-admin-multiple-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin1.code}</domain:contact>
<domain:contact type="admin">#{admin2.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert_equal 1, domain.admin_contacts.count, "Should have only one admin contact"
assert_equal admin2.code, domain.admin_contacts.first.code, "Should keep the non-duplicate admin"
assert response.body.include? "Admin contact #{admin1.code} was discarded as duplicate;"
end
def test_domain_with_duplicate_admin_and_tech
registrant = contacts(:acme_ltd).becomes(Registrant)
admin = Contact.create!(
name: 'Duplicate Admin Tech Test',
code: 'duplicate-admin-003',
email: 'admin-tech@test.com',
phone: '+123.4567890',
ident: '12346X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
tech = Contact.create!(
name: admin.name,
code: 'duplicate-tech-003',
email: admin.email,
phone: admin.phone,
ident: admin.ident,
ident_type: admin.ident_type,
ident_country_code: admin.ident_country_code,
registrar: registrars(:bestnames)
)
name = "domain-admin-tech-duplicate-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin.code}</domain:contact>
<domain:contact type="tech">#{tech.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert_equal 1, domain.admin_contacts.count, "Should have one admin contact"
assert_equal admin.code, domain.admin_contacts.first.code, "Should keep the admin contact"
assert_empty domain.tech_contacts, "Tech contacts should be empty due to duplication with admin"
assert response.body.include? "Tech contact #{tech.code} was discarded as duplicate;"
end
def test_domain_with_duplicate_one_admin_one_tech
registrant = contacts(:acme_ltd).becomes(Registrant)
admin1 = Contact.create!(
name: 'First Admin',
code: 'duplicate-admin-004',
email: 'first-admin@test.com',
phone: '+123.4567890',
ident: '12347X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
admin2 = contacts(:william)
tech1 = Contact.create!(
name: admin1.name,
code: 'duplicate-tech-004',
email: admin1.email,
phone: admin1.phone,
ident: admin1.ident,
ident_type: admin1.ident_type,
ident_country_code: admin1.ident_country_code,
registrar: registrars(:bestnames)
)
tech2 = contacts(:jack)
name = "domain-one-admin-one-tech-dup-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin1.code}</domain:contact>
<domain:contact type="admin">#{admin2.code}</domain:contact>
<domain:contact type="tech">#{tech1.code}</domain:contact>
<domain:contact type="tech">#{tech2.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert_equal 2, domain.admin_contacts.count, "Should have both admin contacts"
tech_contacts = domain.tech_contacts
assert_equal 1, tech_contacts.count, "Should have only the non-duplicate tech contact"
assert_equal tech2.code, tech_contacts.first.code, "Should keep the non-duplicate tech contact"
assert response.body.include? "Tech contact #{tech1.code} was discarded as duplicate;"
end
def test_domain_with_duplicate_registrant_admin_tech
duplicate_contact = Contact.create!(
name: 'Full Duplicate Test',
code: 'duplicate-005',
email: 'full-duplicate@test.com',
phone: '+123.5678901',
ident: '12348X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
registrant = duplicate_contact.becomes(Registrant)
admin = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-admin-005',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: duplicate_contact.ident_type,
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
tech = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-tech-005',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: duplicate_contact.ident_type,
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
name = "domain-all-duplicates-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin.code}</domain:contact>
<domain:contact type="tech">#{tech.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert_empty domain.admin_contacts, "Admin contacts should be empty due to duplication"
assert_empty domain.tech_contacts, "Tech contacts should be empty due to duplication"
assert response.body.include? "Admin contact #{admin.code} was discarded as duplicate;"
assert response.body.include? "Tech contact #{tech.code} was discarded as duplicate;"
end
def test_domain_with_duplicate_registrant_one_admin_one_tech
duplicate_contact = Contact.create!(
name: 'Partial Duplicate Test',
code: 'duplicate-006',
email: 'partial-duplicate@test.com',
phone: '+123.6789012',
ident: '12349X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
registrant = duplicate_contact.becomes(Registrant)
admin1 = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-admin-006',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: 'priv',
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
admin2 = contacts(:jack)
admin2.ident_type = 'priv'
admin2.save!
tech1 = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-tech-006',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: duplicate_contact.ident_type,
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
tech2 = contacts(:william)
name = "domain-partial-duplicates-#{Time.now.to_i}.#{dns_zones(:one).origin}"
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<create>
<domain:create xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{name}</domain:name>
<domain:registrant>#{registrant.code}</domain:registrant>
<domain:contact type="admin">#{admin1.code}</domain:contact>
<domain:contact type="admin">#{admin2.code}</domain:contact>
<domain:contact type="tech">#{tech1.code}</domain:contact>
<domain:contact type="tech">#{tech2.code}</domain:contact>
</domain:create>
</create>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
assert_difference 'Domain.count', 1 do
post epp_create_path, params: { frame: request_xml },
headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
end
response_xml = Nokogiri::XML(response.body)
assert_correct_against_schema response_xml
assert_epp_response :completed_successfully
domain = Domain.find_by(name: name)
assert_not_nil domain, "Domain should have been created"
assert_equal 1, domain.admin_contacts.count, "Should have only the non-duplicate admin contact"
assert_equal admin2.code, domain.admin_contacts.first.code, "Should keep the non-duplicate admin contact"
assert_equal 1, domain.tech_contacts.count, "Should have only the non-duplicate tech contact"
assert_equal tech2.code, domain.tech_contacts.first.code, "Should keep the non-duplicate tech contact"
assert response.body.include? "Admin contact #{admin1.code} was discarded as duplicate;"
assert response.body.include? "Tech contact #{tech1.code} was discarded as duplicate;"
end
end

View file

@ -1091,6 +1091,229 @@ class EppDomainUpdateBaseTest < EppTestCase
assert_epp_response :object_status_prohibits_operation
end
# UPDATE TESTS FOR DUPLICATE CONTACTS
def test_update_domain_with_duplicate_registrant_and_single_admin
domain = domains(:shop)
duplicate_contact = Contact.create!(
name: 'Partial Duplicate Test',
code: 'duplicate-006',
email: 'partial-duplicate@test.com',
phone: '+123.6789012',
ident: '12349X',
ident_type: 'priv',
ident_country_code: 'US',
registrar: registrars(:bestnames)
)
registrant = duplicate_contact.becomes(Registrant)
new_admin = Contact.create!(
name: duplicate_contact.name,
code: 'duplicate-admin-006',
email: duplicate_contact.email,
phone: duplicate_contact.phone,
ident: duplicate_contact.ident,
ident_type: 'priv',
ident_country_code: duplicate_contact.ident_country_code,
registrar: registrars(:bestnames)
)
domain.update(registrant: registrant) && domain.reload
old_admin = domain.admin_contacts.first
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<update>
<domain:update xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{domain.name}</domain:name>
<domain:chg/>
<domain:add>
<domain:contact type="admin">#{new_admin.code}</domain:contact>
</domain:add>
<domain:rem>
<domain:contact type="admin">#{old_admin.code}</domain:contact>
</domain:rem>
</domain:update>
</update>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
post epp_update_path(domain_name: domain.name), params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => 'session=api_bestnames' }
domain.reload
assert_epp_response :completed_successfully
assert response.body.include? "Admin contact #{new_admin.code} was discarded as duplicate;"
end
def test_update_domain_with_registrant_admin_tech_all_duplicates
domain = domains(:airport)
initial_admin_contact = contacts(:jane)
initial_tech_contact = contacts(:william)
domain.admin_contacts = [initial_admin_contact]
domain.tech_contacts = [initial_tech_contact]
domain.save!
domain.reload
base_duplicate_data_contact = Contact.create!(
name: 'All Roles Are The Same Person',
code: "base-all-roles-#{SecureRandom.hex(3)}",
email: 'all.roles.same.person@example.com',
phone: '+1.999888777',
ident: 'ARSAMEP123',
ident_type: 'priv',
ident_country_code: 'US',
registrar: domain.registrar
)
new_registrant = base_duplicate_data_contact.becomes(Registrant)
domain.update!(registrant: new_registrant)
domain.reload
new_admin_being_added = Contact.create!(
name: base_duplicate_data_contact.name,
code: "admin-dup-all-roles-#{SecureRandom.hex(3)}",
email: base_duplicate_data_contact.email,
phone: base_duplicate_data_contact.phone,
ident: base_duplicate_data_contact.ident,
ident_type: base_duplicate_data_contact.ident_type,
ident_country_code: base_duplicate_data_contact.ident_country_code,
registrar: domain.registrar
)
new_tech_being_added = Contact.create!(
name: base_duplicate_data_contact.name,
code: "tech-dup-all-roles-#{SecureRandom.hex(3)}",
email: base_duplicate_data_contact.email,
phone: base_duplicate_data_contact.phone,
ident: base_duplicate_data_contact.ident,
ident_type: base_duplicate_data_contact.ident_type,
ident_country_code: base_duplicate_data_contact.ident_country_code,
registrar: domain.registrar
)
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<update>
<domain:update xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{domain.name}</domain:name>
<domain:chg/>
<domain:add>
<domain:contact type="admin">#{new_admin_being_added.code}</domain:contact>
<domain:contact type="tech">#{new_tech_being_added.code}</domain:contact>
</domain:add>
<domain:rem>
<domain:contact type="admin">#{initial_admin_contact.code}</domain:contact>
<domain:contact type="tech">#{initial_tech_contact.code}</domain:contact>
</domain:rem>
</domain:update>
</update>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
post epp_update_path(domain_name: domain.name), params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => "session=api_bestnames" }
domain.reload
assert_epp_response :completed_successfully
assert response.body.include? "Admin contact #{new_admin_being_added.code} was discarded as duplicate;"
assert response.body.include? "Tech contact #{new_tech_being_added.code} was discarded as duplicate;"
end
def test_update_domain_with_duplicate_admin_and_tech_registrant_is_different
domain = domains(:library)
initial_admin_contact = contacts(:john)
initial_tech_contact = contacts(:william)
domain.admin_contacts = [initial_admin_contact]
domain.tech_contacts = [initial_tech_contact]
domain.save!
domain.reload
admin_tech_duplicate_base = Contact.create!(
name: 'Admin And Tech Are Same Person',
code: "base-adm-tech-#{SecureRandom.hex(3)}",
email: 'admin.tech.same.person@example.com',
phone: '+1.555444333',
ident: 'ATSAMEP456',
ident_type: 'priv',
ident_country_code: 'US',
registrar: domain.registrar
)
new_admin_being_added = Contact.create!(
name: admin_tech_duplicate_base.name,
code: "admin-dup-adm-tech-#{SecureRandom.hex(3)}",
email: admin_tech_duplicate_base.email,
phone: admin_tech_duplicate_base.phone,
ident: admin_tech_duplicate_base.ident,
ident_type: admin_tech_duplicate_base.ident_type,
ident_country_code: admin_tech_duplicate_base.ident_country_code,
registrar: domain.registrar
)
new_tech_being_added_and_skipped = Contact.create!(
name: admin_tech_duplicate_base.name,
code: "tech-dup-adm-tech-#{SecureRandom.hex(3)}",
email: admin_tech_duplicate_base.email,
phone: admin_tech_duplicate_base.phone,
ident: admin_tech_duplicate_base.ident,
ident_type: admin_tech_duplicate_base.ident_type,
ident_country_code: admin_tech_duplicate_base.ident_country_code,
registrar: domain.registrar
)
request_xml = <<-XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<epp xmlns="#{Xsd::Schema.filename(for_prefix: 'epp-ee', for_version: '1.0')}">
<command>
<update>
<domain:update xmlns:domain="#{Xsd::Schema.filename(for_prefix: 'domain-ee', for_version: '1.2')}">
<domain:name>#{domain.name}</domain:name>
<domain:chg/>
<domain:add>
<domain:contact type="admin">#{new_admin_being_added.code}</domain:contact>
<domain:contact type="tech">#{new_tech_being_added_and_skipped.code}</domain:contact>
</domain:add>
<domain:rem>
<domain:contact type="admin">#{initial_admin_contact.code}</domain:contact>
<domain:contact type="tech">#{initial_tech_contact.code}</domain:contact>
</domain:rem>
</domain:update>
</update>
<extension>
<eis:extdata xmlns:eis="#{Xsd::Schema.filename(for_prefix: 'eis', for_version: '1.0')}">
<eis:legalDocument type="pdf">#{'test' * 2000}</eis:legalDocument>
</eis:extdata>
</extension>
</command>
</epp>
XML
post epp_update_path(domain_name: domain.name), params: { frame: request_xml }, headers: { 'HTTP_COOKIE' => "session=api_bestnames" }
domain.reload
assert_epp_response :completed_successfully
assert response.body.include? "Admin contact #{new_admin_being_added.code} was discarded as duplicate;"
end
private
def assert_verification_and_notification_emails