diff --git a/app/interactions/actions/domain_create.rb b/app/interactions/actions/domain_create.rb index 3f90eb5eb..00af6f856 100644 --- a/app/interactions/actions/domain_create.rb +++ b/app/interactions/actions/domain_create.rb @@ -185,7 +185,6 @@ module Actions def validate_ns_records return unless domain.nameservers.any? - return unless dns_validation_enabled? result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'NS') return if result[:errors].blank? @@ -195,7 +194,6 @@ module Actions def validate_dns_records return unless domain.dnskeys.any? - return unless dns_validation_enabled? result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'DNSKEY') return if result[:errors].blank? @@ -228,13 +226,6 @@ module Actions end end - def dns_validation_enabled? - # Enable DNS validation in production or when explicitly enabled - # Disabled in test environment by default unless explicitly enabled - return ENV['DNS_VALIDATION_ENABLED'] == 'true' if Rails.env.test? - Rails.env.production? || ENV['DNS_VALIDATION_ENABLED'] == 'true' - end - def validation_process_errored? return if domain.valid? diff --git a/app/interactions/actions/domain_transfer.rb b/app/interactions/actions/domain_transfer.rb index f72f7b5d7..fdb804362 100644 --- a/app/interactions/actions/domain_transfer.rb +++ b/app/interactions/actions/domain_transfer.rb @@ -17,9 +17,10 @@ module Actions # return domain.pending_transfer if domain.pending_transfer # attach_legal_document(::Deserializers::Xml::LegalDocument.new(frame).call) - return if domain.errors[:epp_errors].any? + return false if domain.errors[:epp_errors].any? commit + true end def domain_exists? @@ -34,6 +35,8 @@ module Actions validate_registrar validate_eligilibty validate_not_discarded + validate_ns_records + validate_dns_records end def valid_transfer_code? @@ -62,6 +65,30 @@ module Actions domain.add_epp_error('2106', nil, nil, 'Object is not eligible for transfer') end + def validate_ns_records + return unless domain.nameservers.any? + + result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'NS') + return if result[:errors].blank? + + assign_dns_validation_error(result[:errors]) + end + + def validate_dns_records + return unless domain.dnskeys.any? + + result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'DNSKEY') + return if result[:errors].blank? + + assign_dns_validation_error(result[:errors]) + end + + def assign_dns_validation_error(errors) + errors.each do |error| + domain.add_epp_error('2306', nil, nil, error) + end + end + def commit bare_domain = Domain.find(domain.id) ::DomainTransfer.request(bare_domain, user) diff --git a/app/interactions/actions/domain_update.rb b/app/interactions/actions/domain_update.rb index b05a56896..282f2c2ac 100644 --- a/app/interactions/actions/domain_update.rb +++ b/app/interactions/actions/domain_update.rb @@ -101,7 +101,6 @@ module Actions def validate_ns_records return unless domain.nameservers.any? - return unless dns_validation_enabled? result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'NS') return if result[:errors].blank? @@ -111,7 +110,6 @@ module Actions def validate_dns_records return unless domain.dnskeys.any? - return unless dns_validation_enabled? result = DNSValidator.validate(domain: domain, name: domain.name, record_type: 'DNSKEY') return if result[:errors].blank? @@ -125,13 +123,6 @@ module Actions end end - def dns_validation_enabled? - # Enable DNS validation in production or when explicitly enabled - # Disabled in test environment by default unless explicitly enabled - return ENV['DNS_VALIDATION_ENABLED'] == 'true' if Rails.env.test? - Rails.env.production? || ENV['DNS_VALIDATION_ENABLED'] == 'true' - end - def assign_dnssec_modifications @dnskeys = [] params[:dns_keys].each do |key| diff --git a/test/integration/api/domain_admin_contacts_test.rb b/test/integration/api/domain_admin_contacts_test.rb index 8064483bc..34dd608ea 100644 --- a/test/integration/api/domain_admin_contacts_test.rb +++ b/test/integration/api/domain_admin_contacts_test.rb @@ -12,6 +12,15 @@ class APIDomainAdminContactsTest < ApplicationIntegrationTest ident_country_code: @admin_current.ident_country_code) adapter = ENV["shunter_default_adapter"].constantize.new adapter&.clear! + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_replace_all_admin_contacts_when_ident_data_doesnt_match diff --git a/test/integration/api/domain_contacts_test.rb b/test/integration/api/domain_contacts_test.rb index 3301326d0..721c8a6fe 100644 --- a/test/integration/api/domain_contacts_test.rb +++ b/test/integration/api/domain_contacts_test.rb @@ -1,6 +1,17 @@ require 'test_helper' class APIDomainContactsTest < ApplicationIntegrationTest + def setup + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) + end + def test_replace_all_tech_contacts_of_the_current_registrar patch '/repp/v1/domains/contacts', params: { current_contact_id: 'william-001', new_contact_id: 'john-001' }, diff --git a/test/integration/api/domain_transfers_test.rb b/test/integration/api/domain_transfers_test.rb index c56417f4d..f242bf62f 100644 --- a/test/integration/api/domain_transfers_test.rb +++ b/test/integration/api/domain_transfers_test.rb @@ -6,10 +6,16 @@ class APIDomainTransfersTest < ApplicationIntegrationTest @new_registrar = registrars(:goodnames) @original_transfer_wait_time = Setting.transfer_wait_time Setting.transfer_wait_time = 0 # Auto-approval + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } end teardown do Setting.transfer_wait_time = @original_transfer_wait_time + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_creates_new_domain_transfer diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index 1c1c16a01..cad5b98ef 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -1,6 +1,17 @@ require 'test_helper' class APINameserversPutTest < ApplicationIntegrationTest + def setup + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) + end + def test_replaces_registrar_nameservers old_nameserver_ids = [nameservers(:shop_ns1).id, nameservers(:airport_ns1).id, diff --git a/test/integration/epp/domain/update/base_test.rb b/test/integration/epp/domain/update/base_test.rb index a8c2045c9..22a044324 100644 --- a/test/integration/epp/domain/update/base_test.rb +++ b/test/integration/epp/domain/update/base_test.rb @@ -13,11 +13,17 @@ class EppDomainUpdateBaseTest < EppTestCase adapter = ENV["shunter_default_adapter"].constantize.new adapter&.clear! + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } end teardown do Setting.request_confirmation_on_registrant_change_enabled = @original_registrant_change_verification + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_update_dnskey_with_invalid_alg diff --git a/test/integration/epp/domain/update/rem_dns_test.rb b/test/integration/epp/domain/update/rem_dns_test.rb index 6e079b126..64559a054 100644 --- a/test/integration/epp/domain/update/rem_dns_test.rb +++ b/test/integration/epp/domain/update/rem_dns_test.rb @@ -12,11 +12,17 @@ class EppDomainUpdateRemDnsTest < EppTestCase @original_registrant_change_verification = Setting.request_confirmation_on_registrant_change_enabled ActionMailer::Base.deliveries.clear + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } end teardown do Setting.request_confirmation_on_registrant_change_enabled = @original_registrant_change_verification + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_remove_dnskey_if_explicitly_set diff --git a/test/integration/epp/domain/update/replace_dns_test.rb b/test/integration/epp/domain/update/replace_dns_test.rb index 62dcbc299..0a15398a8 100644 --- a/test/integration/epp/domain/update/replace_dns_test.rb +++ b/test/integration/epp/domain/update/replace_dns_test.rb @@ -1,6 +1,17 @@ require 'test_helper' class EppDomainUpdateReplaceDnsTest < EppTestCase + def setup + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) + end + def test_parsed_response_for_dnskey_with_spaces_in_request doc = Nokogiri::XML::Document.parse(schema_update) params = { parsed_frame: doc } diff --git a/test/integration/repp/v1/domains/contacts_test.rb b/test/integration/repp/v1/domains/contacts_test.rb index df472bad4..2cff54880 100644 --- a/test/integration/repp/v1/domains/contacts_test.rb +++ b/test/integration/repp/v1/domains/contacts_test.rb @@ -41,7 +41,8 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest end def test_can_add_new_admin_contacts - new_contact = contacts(:john) + DNSValidator.stub :validate, { errors: [] } do + new_contact = contacts(:john) refute @domain.admin_contacts.find_by(code: new_contact.code).present? payload = { contacts: [ { code: new_contact.code, type: 'admin' } ] } @@ -52,10 +53,12 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert @domain.admin_contacts.find_by(code: new_contact.code).present? + end end def test_can_add_new_tech_contacts - new_contact = contacts(:john) + DNSValidator.stub :validate, { errors: [] } do + new_contact = contacts(:john) refute @domain.tech_contacts.find_by(code: new_contact.code).present? payload = { contacts: [ { code: new_contact.code, type: 'tech' } ] } @@ -67,10 +70,12 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest @domain.reload assert @domain.tech_contacts.find_by(code: new_contact.code).present? + end end def test_can_remove_admin_contacts - Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) + DNSValidator.stub :validate, { errors: [] } do + Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) contact = contacts(:john) payload = { contacts: [ { code: contact.code, type: 'admin' } ] } @@ -85,10 +90,12 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] refute @domain.admin_contacts.find_by(code: contact.code).present? + end end def test_can_remove_tech_contacts - Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) + DNSValidator.stub :validate, { errors: [] } do + Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) contact = contacts(:john) payload = { contacts: [ { code: contact.code, type: 'tech' } ] } @@ -106,10 +113,12 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] refute @domain.tech_contacts.find_by(code: contact.code).present? + end end def test_can_remove_all_admin_contacts_for_private_registrant - Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) + DNSValidator.stub :validate, { errors: [] } do + Spy.on_instance_method(Actions::DomainUpdate, :validate_email).and_return(true) @domain.registrant.update!(ident_type: 'priv') @domain.reload @@ -126,6 +135,7 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_equal 1000, json[:code] assert_empty @domain.admin_contacts + end end def test_can_not_remove_one_and_only_contact @@ -183,10 +193,11 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest end def test_can_remove_admin_contact_for_adult_private_registrant - @domain.registrant.update!( - ident_type: 'birthday', - ident: (Time.zone.now - 20.years).strftime('%Y-%m-%d') - ) + DNSValidator.stub :validate, { errors: [] } do + @domain.registrant.update!( + ident_type: 'birthday', + ident: (Time.zone.now - 20.years).strftime('%Y-%m-%d') + ) @domain.reload assert @domain.registrant.priv? @@ -199,6 +210,7 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_response :ok assert_equal 1000, json[:code] assert_empty @domain.admin_contacts + end end def test_cannot_remove_admin_contact_for_underage_estonian_id @@ -222,11 +234,12 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest end def test_can_remove_admin_contact_for_adult_estonian_id - @domain.registrant.update!( - ident_type: 'priv', - ident: '38903111310', - ident_country_code: 'EE' - ) + DNSValidator.stub :validate, { errors: [] } do + @domain.registrant.update!( + ident_type: 'priv', + ident: '38903111310', + ident_country_code: 'EE' + ) @domain.reload assert @domain.registrant.priv? @@ -239,5 +252,6 @@ class ReppV1DomainsContactsTest < ActionDispatch::IntegrationTest assert_response :ok assert_equal 1000, json[:code] assert_empty @domain.admin_contacts + end end end diff --git a/test/integration/repp/v1/domains/create_test.rb b/test/integration/repp/v1/domains/create_test.rb index 1fae52aae..aa1fb45ed 100644 --- a/test/integration/repp/v1/domains/create_test.rb +++ b/test/integration/repp/v1/domains/create_test.rb @@ -8,6 +8,16 @@ class ReppV1DomainsCreateTest < ActionDispatch::IntegrationTest token = "Basic #{token}" @auth_headers = { 'Authorization' => token } + + # Mock DNSValidator to return success by default + # Individual tests can override this if they need to test DNS validation + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_creates_new_domain_successfully diff --git a/test/integration/repp/v1/domains/dnssec_test.rb b/test/integration/repp/v1/domains/dnssec_test.rb index 697970063..57ec39598 100644 --- a/test/integration/repp/v1/domains/dnssec_test.rb +++ b/test/integration/repp/v1/domains/dnssec_test.rb @@ -11,6 +11,15 @@ class ReppV1DomainsDnssecTest < ActionDispatch::IntegrationTest adapter = ENV["shunter_default_adapter"].constantize.new adapter&.clear! + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_shows_dnssec_keys_associated_with_domain diff --git a/test/integration/repp/v1/domains/nameservers_test.rb b/test/integration/repp/v1/domains/nameservers_test.rb index b45863e0c..4fe453156 100644 --- a/test/integration/repp/v1/domains/nameservers_test.rb +++ b/test/integration/repp/v1/domains/nameservers_test.rb @@ -11,6 +11,15 @@ class ReppV1DomainsNameserversTest < ActionDispatch::IntegrationTest adapter = ENV["shunter_default_adapter"].constantize.new adapter&.clear! + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_can_add_new_nameserver diff --git a/test/integration/repp/v1/domains/statuses_test.rb b/test/integration/repp/v1/domains/statuses_test.rb index ee2cb445b..d44dbf2d4 100644 --- a/test/integration/repp/v1/domains/statuses_test.rb +++ b/test/integration/repp/v1/domains/statuses_test.rb @@ -11,6 +11,15 @@ class ReppV1DomainsStatusesTest < ActionDispatch::IntegrationTest adapter = ENV["shunter_default_adapter"].constantize.new adapter&.clear! + + # Mock DNSValidator to return success + @original_validate = DNSValidator.method(:validate) + DNSValidator.define_singleton_method(:validate) { |**args| { errors: [] } } + end + + def teardown + # Restore original validate method + DNSValidator.define_singleton_method(:validate, @original_validate) end def test_client_hold_can_be_added diff --git a/test/integration/repp/v1/domains/transfer_test.rb b/test/integration/repp/v1/domains/transfer_test.rb index fdcbe41d7..153b310d3 100644 --- a/test/integration/repp/v1/domains/transfer_test.rb +++ b/test/integration/repp/v1/domains/transfer_test.rb @@ -171,4 +171,218 @@ class ReppV1DomainsTransferTest < ActionDispatch::IntegrationTest ENV["shunter_default_threshold"] = '10000' ENV["shunter_enabled"] = 'false' end + + def test_transfers_domain_with_valid_dns_records + # Add nameservers to the domain + @domain.nameservers.create!(hostname: 'ns1.example.com', ipv4: ['192.0.2.1']) + @domain.nameservers.create!(hostname: 'ns2.example.com', ipv4: ['192.0.2.2']) + + # Mock successful DNS validation for NS records + DNSValidator.stub :validate, { errors: [] } do + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.registrar, @user.registrar + end + end + + def test_fails_transfer_with_invalid_nameserver_records + # Add nameservers to the domain + @domain.nameservers.create!(hostname: 'ns1.example.com', ipv4: ['192.0.2.1']) + @domain.nameservers.create!(hostname: 'ns2.example.com', ipv4: ['192.0.2.2']) + + # Mock DNS validation failure for NS records + dns_error = 'Nameserver ns1.example.com is not authoritative for domain' + DNSValidator.stub :validate, { errors: [dns_error] } do + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :bad_request + assert_equal 2306, json[:code] + assert_equal dns_error, json[:message] + + # Domain should not be transferred + refute @domain.registrar == @user.registrar + end + end + + def test_transfers_domain_with_valid_dnssec_records + # Add DNSSEC keys to the domain + @domain.dnskeys.create!( + flags: 257, + protocol: 3, + alg: 8, + public_key: 'AwEAAddt2AkLfYGKgiEZB5SmIF8EvrjxNMH6HtxWEA4RJ9Ao6LCRHzfK' + ) + + # Mock successful DNS validation for DNSKEY records + DNSValidator.stub :validate, { errors: [] } do + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.registrar, @user.registrar + end + end + + def test_fails_transfer_with_invalid_dnssec_records + # Add DNSSEC keys to the domain + @domain.dnskeys.create!( + flags: 257, + protocol: 3, + alg: 8, + public_key: 'AwEAAddt2AkLfYGKgiEZB5SmIF8EvrjxNMH6HtxWEA4RJ9Ao6LCRHzfK' + ) + + # Mock DNS validation failure for DNSKEY records + dns_error = 'DNSKEY record not found in DNS' + DNSValidator.stub :validate, { errors: [dns_error] } do + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :bad_request + assert_equal 2306, json[:code] + assert_equal dns_error, json[:message] + + # Domain should not be transferred + refute @domain.registrar == @user.registrar + end + end + + def test_transfers_domain_without_nameservers + # Ensure domain has no nameservers + @domain.nameservers.destroy_all + + # Should transfer successfully without DNS validation + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.registrar, @user.registrar + end + + def test_transfers_domain_without_dnssec + # Ensure domain has no DNSSEC keys + @domain.dnskeys.destroy_all + + # Should transfer successfully without DNSSEC validation + payload = { transfer: { transfer_code: @domain.transfer_code } } + post "/repp/v1/domains/#{@domain.name}/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + @domain.reload + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + assert_equal @domain.registrar, @user.registrar + end + + def test_bulk_transfer_with_dns_validation + domain2 = domains(:metro) + + # Add minimum required nameservers to both domains (2 nameservers required) + @domain.nameservers.create!(hostname: 'ns1.example.com', ipv4: ['192.0.2.1']) + @domain.nameservers.create!(hostname: 'ns2.example.com', ipv4: ['192.0.2.2']) + + domain2.nameservers.create!(hostname: 'ns1.example.org', ipv4: ['192.0.2.10']) + domain2.nameservers.create!(hostname: 'ns2.example.org', ipv4: ['192.0.2.11']) + + # Mock DNS validation - success for both domains + DNSValidator.stub :validate, { errors: [] } do + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code }, + { "domain_name": domain2.name, "transfer_code": domain2.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + assert_equal 'Command completed successfully', json[:message] + + # Both domains should be in success list + assert_equal 2, json[:data][:success].length + assert json[:data][:success].any? { |d| d[:domain_name] == @domain.name } + assert json[:data][:success].any? { |d| d[:domain_name] == domain2.name } + + @domain.reload + domain2.reload + + assert @domain.registrar == @user.registrar + assert domain2.registrar == @user.registrar + end + end + + def test_bulk_transfer_with_mixed_dns_validation_results + domain2 = domains(:metro) + + # Add minimum required nameservers to both domains (2 nameservers required) + @domain.nameservers.create!(hostname: 'ns1.example.com', ipv4: ['192.0.2.1']) + @domain.nameservers.create!(hostname: 'ns2.example.com', ipv4: ['192.0.2.2']) + + domain2.nameservers.create!(hostname: 'ns1.example.org', ipv4: ['192.0.2.10']) + domain2.nameservers.create!(hostname: 'ns2.example.org', ipv4: ['192.0.2.11']) + + # Mock DNS validation - fail for first domain, succeed for second + validation_results = { + @domain.name => { errors: ['Nameserver ns1.example.com is not authoritative'] }, + domain2.name => { errors: [] } + } + + DNSValidator.stub :validate, ->(domain:, **) { + validation_results[domain.name] || { errors: [] } + } do + payload = { + "data": { + "domain_transfers": [ + { "domain_name": @domain.name, "transfer_code": @domain.transfer_code }, + { "domain_name": domain2.name, "transfer_code": domain2.transfer_code } + ] + } + } + + post "/repp/v1/domains/transfer", headers: @auth_headers, params: payload + json = JSON.parse(response.body, symbolize_names: true) + + assert_response :ok + assert_equal 1000, json[:code] + + # First domain should fail, second should succeed + assert_equal 1, json[:data][:success].length + assert_equal domain2.name, json[:data][:success][0][:domain_name] + + assert_equal 1, json[:data][:failed].length + assert_equal @domain.name, json[:data][:failed][0][:domain_name] + assert json[:data][:failed][0][:errors][:msg].include?('not authoritative') + + @domain.reload + domain2.reload + + # Only domain2 should be transferred + refute @domain.registrar == @user.registrar + assert domain2.registrar == @user.registrar + end + end end diff --git a/test/integration/repp/v1/domains/update_test.rb b/test/integration/repp/v1/domains/update_test.rb index 093a2b338..491cad117 100644 --- a/test/integration/repp/v1/domains/update_test.rb +++ b/test/integration/repp/v1/domains/update_test.rb @@ -11,6 +11,7 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest end def test_updates_transfer_code_for_domain + DNSValidator.stub :validate, { errors: [] } do @auth_headers['Content-Type'] = 'application/json' new_auth_code = 'aisdcbkabcsdnc' @@ -28,12 +29,14 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest assert_equal 'Command completed successfully', json[:message] assert new_auth_code, @domain.auth_info + end end def test_domain_pending_update_on_registrant_change - Setting.request_confirmation_on_registrant_change_enabled = true + DNSValidator.stub :validate, { errors: [] } do + Setting.request_confirmation_on_registrant_change_enabled = true - @auth_headers['Content-Type'] = 'application/json' + @auth_headers['Content-Type'] = 'application/json' new_registrant = contacts(:william) refute @domain.registrant == new_registrant @@ -54,12 +57,14 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest refute @domain.registrant.code == new_registrant.code assert @domain.statuses.include? DomainStatus::PENDING_UPDATE + end end def test_replaces_registrant_when_verified - Setting.request_confirmation_on_registrant_change_enabled = true + DNSValidator.stub :validate, { errors: [] } do + Setting.request_confirmation_on_registrant_change_enabled = true - @auth_headers['Content-Type'] = 'application/json' + @auth_headers['Content-Type'] = 'application/json' new_registrant = contacts(:william) refute @domain.registrant == new_registrant old_transfer_code = @domain.transfer_code @@ -84,12 +89,14 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest refute_equal old_transfer_code, @domain.transfer_code assert @domain.registrant.code == new_registrant.code refute @domain.statuses.include? DomainStatus::PENDING_UPDATE + end end def test_adds_epp_error_when_reserved_pw_is_missing_for_disputed_domain - Dispute.create!(domain_name: @domain.name, password: '1234567890', starts_at: Time.zone.now, expires_at: Time.zone.now + 5.days) + DNSValidator.stub :validate, { errors: [] } do + Dispute.create!(domain_name: @domain.name, password: '1234567890', starts_at: Time.zone.now, expires_at: Time.zone.now + 5.days) - @auth_headers['Content-Type'] = 'application/json' + @auth_headers['Content-Type'] = 'application/json' payload = { domain: { reserved_pw: nil, @@ -102,12 +109,14 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest assert_response :bad_request assert_equal 2304, json[:code] assert_equal 'Required parameter missing; reservedpw element required for dispute domains', json[:message] + end end def test_adds_epp_error_when_reserved_pw_is_invalid_for_disputed_domain - Dispute.create!(domain_name: @domain.name, password: '1234567890', starts_at: Time.zone.now, expires_at: Time.zone.now + 5.days) + DNSValidator.stub :validate, { errors: [] } do + Dispute.create!(domain_name: @domain.name, password: '1234567890', starts_at: Time.zone.now, expires_at: Time.zone.now + 5.days) - @auth_headers['Content-Type'] = 'application/json' + @auth_headers['Content-Type'] = 'application/json' payload = { domain: { reserved_pw: 'invalid', @@ -120,6 +129,7 @@ class ReppV1DomainsUpdateTest < ActionDispatch::IntegrationTest assert_response :bad_request assert_equal 2202, json[:code] assert_equal 'Invalid authorization information; invalid reserved>pw value', json[:message] + end end end