diff --git a/CHANGELOG.md b/CHANGELOG.md index abfa5f05e..153354e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ 06.01.2021 +* IMproved tests whois update for bulk nameserver change [#1739](https://github.com/internetee/registry/issues/1739) +* Bulk ForceDelete funcionality in admin [#1177](https://github.com/internetee/registry/issues/1177) * Reverted Nokogiri bump due to dependency conflicts in production [#1787](https://github.com/internetee/registry/pull/1787) 05.01.2021 diff --git a/app/controllers/admin/mass_actions_controller.rb b/app/controllers/admin/mass_actions_controller.rb new file mode 100644 index 000000000..bc3491faf --- /dev/null +++ b/app/controllers/admin/mass_actions_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Admin + class MassActionsController < BaseController + before_action :authorize_admin + + # GET /admin/mass_actions + def index; end + + # POST /admin/mass_actions + def create + res = MassAction.process(params[:mass_action], params[:entry_list].path) + notice = if res + "#{params[:mass_action]} completed for #{res[:ok]}.\n" \ + "Failed: #{res[:fail]}" + else + "Dataset integrity validation failed for #{params[:mass_action]}" + end + + redirect_to(admin_mass_actions_path, notice: notice) + end + + def authorize_admin + authorize! :manage, :mass_actions + end + end +end diff --git a/app/controllers/registrar/nameservers_controller.rb b/app/controllers/registrar/nameservers_controller.rb index 2a22476be..52c43bb1d 100644 --- a/app/controllers/registrar/nameservers_controller.rb +++ b/app/controllers/registrar/nameservers_controller.rb @@ -48,18 +48,25 @@ class Registrar parsed_response = JSON.parse(response.body, symbolize_names: true) if response.code == '200' - notices = [t('.replaced')] - notices << "#{t('.affected_domains')}: " \ - "#{parsed_response[:data][:affected_domains].join(', ')}" - - flash[:notice] = notices.join(', ') - redirect_to registrar_domains_url + redirect_to(registrar_domains_url, + flash: { notice: compose_notice_message(parsed_response) }) else @api_errors = parsed_response[:message] render file: 'registrar/bulk_change/new', locals: { active_tab: :nameserver } end end + def compose_notice_message(res) + notices = ["#{t('.replaced')}. #{t('.affected_domains')}: " \ + "#{res[:data][:affected_domains].join(', ')}"] + + if res[:data][:skipped_domains] + notices << "#{t('.skipped_domains')}: #{res[:data][:skipped_domains].join(', ')}" + end + + notices.join(', ') + end + def domain_list_from_csv return [] if params[:puny_file].blank? diff --git a/app/controllers/repp/v1/registrar/nameservers_controller.rb b/app/controllers/repp/v1/registrar/nameservers_controller.rb index 47004d97b..39f076e9b 100644 --- a/app/controllers/repp/v1/registrar/nameservers_controller.rb +++ b/app/controllers/repp/v1/registrar/nameservers_controller.rb @@ -5,25 +5,31 @@ module Repp before_action :verify_nameserver_existance, only: %i[update] def update - domains = params[:data][:domains] || [] - affected = current_user.registrar - .replace_nameservers(hostname, - hostname_params[:data][:attributes], - domains: domains) + affected, errored = current_user.registrar + .replace_nameservers(hostname, + hostname_params[:data][:attributes], + domains: domains_from_params) - render_success(data: data_format_for_success(affected)) + render_success(data: data_format_for_success(affected, errored)) rescue ActiveRecord::RecordInvalid => e handle_errors(e.record) end private - def data_format_for_success(affected_domains) + def domains_from_params + return [] unless params[:data][:domains] + + params[:data][:domains].map(&:downcase) + end + + def data_format_for_success(affected_domains, errored_domains) { type: 'nameserver', id: params[:data][:attributes][:hostname], attributes: params[:data][:attributes], affected_domains: affected_domains, + skipped_domains: errored_domains, } end diff --git a/app/interactions/domains/force_delete/base.rb b/app/interactions/domains/force_delete/base.rb index 27601c1d2..d4ad2b820 100644 --- a/app/interactions/domains/force_delete/base.rb +++ b/app/interactions/domains/force_delete/base.rb @@ -10,6 +10,9 @@ module Domains boolean :notify_by_email, default: false, description: 'Do we need to send email notification' + string :reason, + default: nil, + description: 'Which mail template to use explicitly' validates :type, inclusion: { in: %i[fast_track soft] } end diff --git a/app/interactions/domains/force_delete/notify_by_email.rb b/app/interactions/domains/force_delete/notify_by_email.rb index b60f54a5e..e512657b0 100644 --- a/app/interactions/domains/force_delete/notify_by_email.rb +++ b/app/interactions/domains/force_delete/notify_by_email.rb @@ -8,7 +8,7 @@ module Domains send_email domain.update(contact_notification_sent_date: Time.zone.today) else - domain.update(template_name: domain.notification_template) + domain.update(template_name: domain.notification_template(explicit: reason)) end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 31637b8ea..0bee01f9c 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -109,6 +109,7 @@ class Ability can :destroy, :pending can :create, :zonefile can :access, :settings_menu + can :manage, :mass_actions can :manage, BouncedMailAddress end diff --git a/app/models/concerns/domain/force_delete.rb b/app/models/concerns/domain/force_delete.rb index e06da25cc..87e9a957b 100644 --- a/app/models/concerns/domain/force_delete.rb +++ b/app/models/concerns/domain/force_delete.rb @@ -19,7 +19,10 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength end end - def notification_template + def notification_template(explicit: nil) + reason = explicit&.downcase + return reason if %w[invalid_email invalid_phone].include?(reason) + if contact_emails_verification_failed.present? 'invalid_email' elsif registrant.org? @@ -33,9 +36,8 @@ module Concerns::Domain::ForceDelete # rubocop:disable Metrics/ModuleLength statuses.include?(DomainStatus::FORCE_DELETE) end - def schedule_force_delete(type: :fast_track, notify_by_email: false) - Domains::ForceDelete::SetForceDelete.run(domain: self, - type: type, + def schedule_force_delete(type: :fast_track, notify_by_email: false, reason: nil) + Domains::ForceDelete::SetForceDelete.run(domain: self, type: type, reason: reason, notify_by_email: notify_by_email) end diff --git a/app/models/mass_action.rb b/app/models/mass_action.rb new file mode 100644 index 000000000..c9cc1fe14 --- /dev/null +++ b/app/models/mass_action.rb @@ -0,0 +1,42 @@ +class MassAction + def self.process(action_type, entries) + entries = CSV.read(entries, headers: true) + case action_type + when 'force_delete' + process_force_delete(entries) + else + false + end + rescue StandardError + false + end + + def self.process_force_delete(entries) + return false unless force_delete_entries_valid?(entries) + + apply_force_deletes(entries) + end + + def self.apply_force_deletes(entries) + log = { ok: [], fail: [] } + entries.each do |e| + dn = Domain.find_by(name_puny: e['domain_name']) + log[:fail] << e['domain_name'] and next unless dn + + dn.schedule_force_delete(type: :soft, notify_by_email: true, reason: e['delete_reason']) + + log[:ok] << dn.name + end + + log + end + + def self.force_delete_entries_valid?(entries) + entries.each do |e| + reasons = %w[ENTITY_BURIED INVALID_EMAIL INVALID_PHONE] + return false unless e['domain_name'].present? && reasons.include?(e['delete_reason']) + end + + true + end +end diff --git a/app/models/registrar.rb b/app/models/registrar.rb index 149bb7541..168dfdca7 100644 --- a/app/models/registrar.rb +++ b/app/models/registrar.rb @@ -144,22 +144,33 @@ class Registrar < ApplicationRecord # Audit log is needed, therefore no raw SQL def replace_nameservers(hostname, new_attributes, domains: []) transaction do + domain_scope = domains.dup domain_list = [] + failed_list = [] - nameservers.where(hostname: hostname).find_each do |original_nameserver| - next unless domains.include?(original_nameserver.domain.name_puny) || domains.empty? + nameservers.where(hostname: hostname).find_each do |origin| + idn = origin.domain.name + puny = origin.domain.name_puny + next unless domains.include?(idn) || domains.include?(puny) || domains.empty? + + if origin.domain.nameservers.where(hostname: new_attributes[:hostname]).any? + failed_list << idn + next + end new_nameserver = Nameserver.new - new_nameserver.domain = original_nameserver.domain + new_nameserver.domain = origin.domain new_nameserver.attributes = new_attributes new_nameserver.save! - domain_list << original_nameserver.domain.name + domain_scope.delete_if { |i| i == idn || i == puny } + domain_list << idn - original_nameserver.destroy! + origin.destroy! end - domain_list.uniq.sort + self.domains.where(name: domain_list).find_each(&:update_whois_record) if domain_list.any? + [domain_list.uniq.sort, (domain_scope + failed_list).uniq.sort] end end diff --git a/app/views/admin/base/_menu.haml b/app/views/admin/base/_menu.haml index 5853bd3e6..46910afa7 100644 --- a/app/views/admin/base/_menu.haml +++ b/app/views/admin/base/_menu.haml @@ -33,6 +33,7 @@ %li= link_to t('.blocked_domains'), admin_blocked_domains_path %li= link_to t('.reserved_domains'), admin_reserved_domains_path %li= link_to t('.disputed_domains'), admin_disputes_path + %li= link_to t('.bulk_actions'), admin_mass_actions_path %li= link_to t('.bounced_email_addresses'), admin_bounced_mail_addresses_path %li= link_to t('.epp_log'), admin_epp_logs_path(created_after: 'today') %li= link_to t('.repp_log'), admin_repp_logs_path(created_after: 'today') diff --git a/app/views/admin/mass_actions/index.html.erb b/app/views/admin/mass_actions/index.html.erb new file mode 100644 index 000000000..22ddf6bbb --- /dev/null +++ b/app/views/admin/mass_actions/index.html.erb @@ -0,0 +1,19 @@ + + +
+
Bulk Domain Force Delete
+
+

Triggers soft force delete procedure for uploaded domain list. List must be in CSV format. First row of the CSV file must contain column headings with domain_name for the first and delete_reason for the second column. Each domain entry must be on separate line. Domain names are expected to be in punycode format, valid reasons are listed below.

+

Allowed delete reasons: ENTITY_BURIED | INVALID_PHONE | INVALID_EMAIL

+ <%= form_tag admin_mass_actions_path, multipart: true, method: :post do %> + <%= label_tag :entry_list %> + <%= file_field_tag :entry_list, required: true, accept: 'text/csv' %> + <%= hidden_field_tag :mass_action, 'force_delete' %> + <%= hidden_field_tag :authenticity_token, form_authenticity_token %> +
+ <%= submit_tag "Start force delete process", class: 'btn btn-danger', id: 'fd_submit' %> + <% end %> +
+
diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_phone.html.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_phone.html.erb new file mode 100644 index 000000000..746a0e256 --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_phone.html.erb @@ -0,0 +1,47 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.

+ +

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

+ +

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.

+ +

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

+ +

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

+ +

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: +<%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/app/views/mailers/domain_delete_mailer/forced/invalid_phone.text.erb b/app/views/mailers/domain_delete_mailer/forced/invalid_phone.text.erb new file mode 100644 index 000000000..fe61b44d1 --- /dev/null +++ b/app/views/mailers/domain_delete_mailer/forced/invalid_phone.text.erb @@ -0,0 +1,47 @@ +

Lugupeetud domeeni <%= @domain.name %> registreerija/halduskontakt

+ +

Eesti Interneti Sihtasutusele (EIS) on saanud teatavaks, et domeeni <%= @domain.name %> kontakti(de) telefoni number või numbrid on puudulikud.

+ +

Et see olukord on vastuolus .ee domeenireeglitega algatas EIS <%= @delete_period_length %> päeva pikkuse kustutusmenetluse. Menetluse käigus on domeen <%= @expire_warning_period %> esimest päeva internetis kättesaadav.

+ +

Andmete parandamiseks pöörduge palun oma registripidaja <%= @registrar.name %> poole või isiklike ja oma ettevõtte andmete puhul registreerija portaali.

+ +

Kui kontaktandmed ei ole <%= @delete_period_length %> päeva jooksul parandatud, läheb domeen <%= @domain.name %> <%= @domain.force_delete_date %> domeenioksjonile .ee oksjonikeskkonda. Juhul kui domeenile <%= @domain.name %> ei tehta oksjonil 24h möödudes pakkumist, domeen vabaneb ja on registreerimiseks vabalt kättesaadav kõigile huvilistele. Muude võimalike oksjoni tulemuste kohta loe siit.

+ +

Lisaküsimuste korral võtke palun ühendust oma registripidajaga:

+<%= render 'mailers/shared/registrar/registrar.et.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.et.html' %> + +
+ +

Dear registrant/administrative contact of .ee domain,

+ +

Estonian Internet Foundation has learned that contact(s) phone number data of the domain <%= @domain.name %> are invalid.

+ +

Since this is a violation of Estonian domain regulations, <%= @delete_period_length %>-day deletion process has started for the <%= @domain.name %> domain. For the first <%= @expire_warning_period %> days the domain will remain available on the Internet during the deletion process.

+ +

Please, contact your registrar <%= @registrar.name %> with updated contact data, or in case of your personal or business data use .ee portal for registrants

+ +

If the data is not fixed within <%= @delete_period_length %> days, the domain <%= @domain.name %> will go to domain auction on <%= @domain.force_delete_date %> in the .ee auction environment. If no offer is made for the domain <%= @domain.name %> at auction within 24 hours, the domain will be released and made freely available for registration to anyone interested on a first-come, first-served basis. Read more about other potential auction results here.

+ +

Should you have additional questions, please contact your registrar:

+<%= render 'mailers/shared/registrar/registrar.en.html', registrar: @registrar %> + +<%= render 'mailers/shared/signatures/signature.en.html' %> +
+ +

Уважаемый регистрант/административный контакт домена .ee

+ +

Целевому учреждению Eesti Internet (EIS) стало известно, что контактные данные домена <%= @domain.name %> неверны - телефонные номера.

+ +

Так как это является нарушением Правил домена .ee, <%= @delete_period_length %>-дневный процесс удаления начат для доменного имени <%= @domain.name %>. В течение первых <%= @expire_warning_period %> дней домен будет доступен в интернете.

+ +

Для уточнения контактных данных, пожалуйста, свяжитесь с регистратором <%= @registrar.name %>, либо воспользуйтесь порталом для регистрантов

+ +

Если контактные данные не будут исправлены в течение <%= @delete_period_length %> дней, домен <%= @domain.name %> отправится <%= @domain.force_delete_date %> на доменный аукцион в аукционной среде.ee. Если в течение 24 часов в отношении домена <%= @domain.name %> е поступит предложений, домен освободится и станет доступным для всех желающих по принципу «кто раньше». О других возможных результатах аукциона читайте здесь.

+ +

В случае возникновения дополнительных вопросов свяжитесь, пожалуйста, со своим регистратором: + <%= render 'mailers/shared/registrar/registrar.ru.html', registrar: @registrar %>

+ +<%= render 'mailers/shared/signatures/signature.ru.html' %> diff --git a/config/locales/admin/menu.en.yml b/config/locales/admin/menu.en.yml index cb1060e6f..52f0a210b 100644 --- a/config/locales/admin/menu.en.yml +++ b/config/locales/admin/menu.en.yml @@ -14,6 +14,7 @@ en: blocked_domains: Blocked domains reserved_domains: Reserved domains disputed_domains: Disputed domains + bulk_actions: Bulk actions bounced_email_addresses: Bounced emails epp_log: EPP log repp_log: REPP log diff --git a/config/locales/registrar/nameservers.en.yml b/config/locales/registrar/nameservers.en.yml index 6cc08f0ab..9c1c2a70e 100644 --- a/config/locales/registrar/nameservers.en.yml +++ b/config/locales/registrar/nameservers.en.yml @@ -4,3 +4,4 @@ en: update: replaced: Nameserver have been successfully replaced affected_domains: Affected domains + skipped_domains: Untouched domains diff --git a/config/routes.rb b/config/routes.rb index 3e0a84334..eb062ca31 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -322,6 +322,7 @@ Rails.application.routes.draw do resources :delayed_jobs resources :epp_logs resources :repp_logs + resources :mass_actions, only: %i[index create] resources :bounced_mail_addresses, only: %i[index show destroy] authenticate :admin_user do diff --git a/test/fixtures/files/mass_actions/invalid_mass_force_delete_list.csv b/test/fixtures/files/mass_actions/invalid_mass_force_delete_list.csv new file mode 100644 index 000000000..ef5e4c0e3 --- /dev/null +++ b/test/fixtures/files/mass_actions/invalid_mass_force_delete_list.csv @@ -0,0 +1,2 @@ +domain_name,delete_reason +sh\á;[]c' diff --git a/test/fixtures/files/mass_actions/valid_mass_force_delete_list.csv b/test/fixtures/files/mass_actions/valid_mass_force_delete_list.csv new file mode 100644 index 000000000..ba88abeb4 --- /dev/null +++ b/test/fixtures/files/mass_actions/valid_mass_force_delete_list.csv @@ -0,0 +1,5 @@ +domain_name,delete_reason +shop.test,ENTITY_BURIED +airport.test,INVALID_PHONE +library.test,INVALID_EMAIL +nonexistant.test,ENTITY_BURIED diff --git a/test/fixtures/files/valid_domains_for_ns_replacement.csv b/test/fixtures/files/valid_domains_for_ns_replacement.csv new file mode 100644 index 000000000..122301ca8 --- /dev/null +++ b/test/fixtures/files/valid_domains_for_ns_replacement.csv @@ -0,0 +1,2 @@ +domain_name +shop.test diff --git a/test/integration/api/nameservers/put_test.rb b/test/integration/api/nameservers/put_test.rb index 3ab4f4dd4..77b01a9b1 100644 --- a/test/integration/api/nameservers/put_test.rb +++ b/test/integration/api/nameservers/put_test.rb @@ -67,7 +67,8 @@ class APINameserversPutTest < ApplicationIntegrationTest attributes: { hostname: 'ns55.bestnames.test', ipv4: ['192.0.2.55'], ipv6: ['2001:db8::55'] }, - affected_domains: ["airport.test", "shop.test"] }}), + affected_domains: ["airport.test", "shop.test"], + skipped_domains: [] }}), JSON.parse(response.body, symbolize_names: true) end diff --git a/test/models/registrar/replace_nameservers_test.rb b/test/models/registrar/replace_nameservers_test.rb index 5bcbb83d1..b4c99bba1 100644 --- a/test/models/registrar/replace_nameservers_test.rb +++ b/test/models/registrar/replace_nameservers_test.rb @@ -10,7 +10,7 @@ class ReplaceNameserversTest < ActiveSupport::TestCase ipv6: '2001:db8::2' } result = @registrar.replace_nameservers('ns1.bestnames.test', new_attributes) - assert_equal(["airport.test", "shop.test"], result) + assert_equal([["airport.test", "shop.test"], []], result) end def test_replace_nameservers_in_bulk_returns_empty_array_for_non_existent_base_nameserver @@ -18,6 +18,22 @@ class ReplaceNameserversTest < ActiveSupport::TestCase ipv6: '2001:db8::2' } result = @registrar.replace_nameservers('ns3.bestnames.test', new_attributes) - assert_equal([], result) + assert_equal([[], []], result) + end + + def test_replace_nameserver_in_bulk_respects_domain_limit_scope + eligible_domain = domains(:shop) + unscoped_domain = domains(:airport) + + new_attributes = { hostname: 'ns-updated1.bestnames.test', ipv4: '192.0.3.1', + ipv6: '2001:db8::2' } + + result = @registrar.replace_nameservers('ns1.bestnames.test', new_attributes, domains: ['shop.test']) + assert_equal([["shop.test"], []], result) + + unscoped_domain.reload + eligible_domain.reload + assert eligible_domain.nameservers.where(hostname: 'ns1.bestnames.test').empty? + assert unscoped_domain.nameservers.where(hostname: 'ns1.bestnames.test').any? end end diff --git a/test/system/admin_area/mass_actions/mass_force_delete_test.rb b/test/system/admin_area/mass_actions/mass_force_delete_test.rb new file mode 100644 index 000000000..d188b35fd --- /dev/null +++ b/test/system/admin_area/mass_actions/mass_force_delete_test.rb @@ -0,0 +1,24 @@ +require 'application_system_test_case' +require 'test_helper' + +class AdminAreaMassActionsForceDeleteTest < ApplicationSystemTestCase + def setup + sign_in users(:admin) + end + + def test_processes_uploaded_valid_csv + visit admin_mass_actions_path + + attach_file('entry_list', Rails.root.join('test', 'fixtures', 'files', 'mass_actions', 'valid_mass_force_delete_list.csv').to_s) + click_link_or_button 'Start force delete process' + assert_text 'force_delete completed for ["shop.test", "airport.test", "library.test"]. Failed: ["nonexistant.test"]' + end + + def test_processes_uploaded_invalid_csv + visit admin_mass_actions_path + + attach_file(:entry_list, Rails.root.join('test', 'fixtures', 'files', 'mass_actions', 'invalid_mass_force_delete_list.csv').to_s) + click_link_or_button 'Start force delete process' + assert_text 'Dataset integrity validation failed for force_delete' + end +end diff --git a/test/system/registrar_area/bulk_change/nameserver_test.rb b/test/system/registrar_area/bulk_change/nameserver_test.rb index 48806df7a..0ba8f7ba2 100644 --- a/test/system/registrar_area/bulk_change/nameserver_test.rb +++ b/test/system/registrar_area/bulk_change/nameserver_test.rb @@ -18,7 +18,8 @@ class RegistrarAreaNameserverBulkChangeTest < ApplicationSystemTestCase .to_return(body: { data: { type: 'nameserver', id: 'new-ns.bestnames.test', - affected_domains: ["airport.test", "shop.test"] + affected_domains: ["airport.test", "shop.test"], + skipped_domains: [] } }.to_json, status: 200) @@ -59,4 +60,38 @@ class RegistrarAreaNameserverBulkChangeTest < ApplicationSystemTestCase assert_field 'ipv4', with: 'ipv4' assert_field 'ipv6', with: 'ipv6' end + + def test_replaces_nameservers_only_for_scoped_domains + request_body = { data: { type: 'nameserver', + id: 'ns1.bestnames.test', + domains: ['shop.test'], + attributes: { hostname: 'new-ns.bestnames.test', + ipv4: %w[192.0.2.55 192.0.2.56], + ipv6: %w[2001:db8::55 2001:db8::56] } } } + request_stub = stub_request(:put, /registrar\/nameservers/).with(body: request_body, + headers: { 'Content-type' => Mime[:json] }, + basic_auth: ['test_goodnames', 'testtest']) + .to_return(body: { data: { + type: 'nameserver', + id: 'new-ns.bestnames.test', + affected_domains: ["shop.test"], + skipped_domains: []}}.to_json, status: 200) + + visit registrar_domains_url + click_link 'Bulk change' + click_link 'Nameserver' + + fill_in 'Old hostname', with: 'ns1.bestnames.test' + fill_in 'New hostname', with: 'new-ns.bestnames.test' + fill_in 'ipv4', with: "192.0.2.55\n192.0.2.56" + fill_in 'ipv6', with: "2001:db8::55\n2001:db8::56" + attach_file :puny_file, Rails.root.join('test', 'fixtures', 'files', 'valid_domains_for_ns_replacement.csv').to_s + + click_on 'Replace nameserver' + + assert_requested request_stub + assert_current_path registrar_domains_path + assert_text 'Nameserver have been successfully replaced' + assert_text 'Affected domains: shop.test' + end end