summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorNick Thomas <nick@gitlab.com>2018-02-06 13:25:46 +0000
committerNick Thomas <nick@gitlab.com>2018-02-23 12:22:29 +0000
commitee68bd9771f671ce7c258a8f5441125f1a9c2d53 (patch)
tree965830e9733bf7ee60e1971c93d1c91b9d584db5 /spec
parent58a312f5097b30a93100de93d06427402d514b48 (diff)
downloadgitlab-ce-ee68bd9771f671ce7c258a8f5441125f1a9c2d53.tar.gz
Add DNS verification to Pages custom domains
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb41
-rw-r--r--spec/factories/pages_domains.rb21
-rw-r--r--spec/features/projects/pages_spec.rb2
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json5
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json5
-rw-r--r--spec/mailers/emails/pages_domains_spec.rb71
-rw-r--r--spec/migrations/enqueue_verify_pages_domain_workers_spec.rb23
-rw-r--r--spec/models/pages_domain_spec.rb144
-rw-r--r--spec/services/notification_service_spec.rb72
-rw-r--r--spec/services/verify_pages_domain_service_spec.rb270
-rw-r--r--spec/workers/pages_domain_verification_cron_worker_spec.rb21
-rw-r--r--spec/workers/pages_domain_verification_worker_spec.rb27
12 files changed, 694 insertions, 8 deletions
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index e9e7d357d9c..2192fd5cae2 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -46,7 +46,46 @@ describe Projects::PagesDomainsController do
post(:create, request_params.merge(pages_domain: pages_domain_params))
end.to change { PagesDomain.count }.by(1)
- expect(response).to redirect_to(project_pages_path(project))
+ created_domain = PagesDomain.reorder(:id).last
+
+ expect(created_domain).to be_present
+ expect(response).to redirect_to(project_pages_domain_path(project, created_domain))
+ end
+ end
+
+ describe 'POST verify' do
+ let(:params) { request_params.merge(id: pages_domain.domain) }
+
+ def stub_service
+ service = double(:service)
+
+ expect(VerifyPagesDomainService).to receive(:new) { service }
+
+ service
+ end
+
+ it 'handles verification success' do
+ expect(stub_service).to receive(:execute).and_return(status: :success)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:notice]).to eq('Successfully verified domain ownership')
+ end
+
+ it 'handles verification failure' do
+ expect(stub_service).to receive(:execute).and_return(status: :failed)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:alert]).to eq('Failed to verify domain ownership')
+ end
+
+ it 'returns a 404 response for an unknown domain' do
+ post :verify, request_params.merge(id: 'unknown-domain')
+
+ expect(response).to have_gitlab_http_status(404)
end
end
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index 61b04708da2..35b44e1c52e 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -1,6 +1,25 @@
FactoryBot.define do
factory :pages_domain, class: 'PagesDomain' do
- domain 'my.domain.com'
+ sequence(:domain) { |n| "my#{n}.domain.com" }
+ verified_at { Time.now }
+ enabled_until { 1.week.from_now }
+
+ trait :disabled do
+ verified_at nil
+ enabled_until nil
+ end
+
+ trait :unverified do
+ verified_at nil
+ end
+
+ trait :reverify do
+ enabled_until { 1.hour.from_now }
+ end
+
+ trait :expired do
+ enabled_until { 1.hour.ago }
+ end
trait :with_certificate do
certificate '-----BEGIN CERTIFICATE-----
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 3f1ef0b2a47..a96f2c186a4 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -60,7 +60,6 @@ feature 'Pages' do
fill_in 'Domain', with: 'my.test.domain.com'
click_button 'Create New Domain'
- expect(page).to have_content('Domains (1)')
expect(page).to have_content('my.test.domain.com')
end
end
@@ -159,7 +158,6 @@ feature 'Pages' do
fill_in 'Key (PEM)', with: certificate_key
click_button 'Create New Domain'
- expect(page).to have_content('Domains (1)')
expect(page).to have_content('my.test.domain.com')
end
end
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index e8c17298b43..ed8ed9085c0 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -4,6 +4,9 @@
"domain": { "type": "string" },
"url": { "type": "uri" },
"project_id": { "type": "integer" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -14,6 +17,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url", "project_id"],
+ "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
index 08db8d47050..b57d544f896 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
@@ -3,6 +3,9 @@
"properties": {
"domain": { "type": "string" },
"url": { "type": "uri" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate": {
"type": "object",
"properties": {
@@ -15,6 +18,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url"],
+ "required": ["domain", "url", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/mailers/emails/pages_domains_spec.rb b/spec/mailers/emails/pages_domains_spec.rb
new file mode 100644
index 00000000000..fe428ea657d
--- /dev/null
+++ b/spec/mailers/emails/pages_domains_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+require 'email_spec'
+
+describe Emails::PagesDomains do
+ include EmailSpec::Matchers
+ include_context 'gitlab email notification'
+
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:user) { project.owner }
+
+ shared_examples 'a pages domain email' do
+ it_behaves_like 'an email sent from GitLab'
+ it_behaves_like 'it should not have Gmail Actions links'
+ it_behaves_like 'a user cannot unsubscribe through footer link'
+
+ it 'has the expected content' do
+ aggregate_failures do
+ is_expected.to have_subject(email_subject)
+ is_expected.to have_body_text(project.human_name)
+ is_expected.to have_body_text(domain.domain)
+ is_expected.to have_body_text domain.url
+ is_expected.to have_body_text project_pages_domain_url(project, domain)
+ is_expected.to have_body_text help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ end
+ end
+ end
+
+ describe '#pages_domain_enabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been enabled" }
+
+ subject { Notify.pages_domain_enabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been enabled' }
+ end
+
+ describe '#pages_domain_disabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been disabled" }
+
+ subject { Notify.pages_domain_disabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been disabled' }
+ end
+
+ describe '#pages_domain_verification_succeeded_email' do
+ let(:email_subject) { "#{project.path} | Verification succeeded for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_succeeded_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'successfully verified' }
+ end
+
+ describe '#pages_domain_verification_failed_email' do
+ let(:email_subject) { "#{project.path} | ACTION REQUIRED: Verification failed for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_failed_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it 'says verification has failed and when the domain is enabled until' do
+ is_expected.to have_body_text 'Verification has failed'
+ is_expected.to have_body_text domain.enabled_until.strftime('%F %T')
+ end
+ end
+end
diff --git a/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
new file mode 100644
index 00000000000..afcaefa0591
--- /dev/null
+++ b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180216121030_enqueue_verify_pages_domain_workers')
+
+describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do
+ around do |example|
+ Sidekiq::Testing.fake! do
+ example.run
+ end
+ end
+
+ describe '#up' do
+ it 'enqueues a verification worker for every domain' do
+ domains = 1.upto(3).map { |i| PagesDomain.create!(domain: "my#{i}.domain.com") }
+
+ expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3)
+
+ enqueued_ids = PagesDomainVerificationWorker.jobs.map { |job| job['args'] }
+ expected_ids = domains.map { |domain| [domain.id] }
+
+ expect(enqueued_ids).to match_array(expected_ids)
+ end
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 9d12f96c642..95713d8b85b 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -1,6 +1,10 @@
require 'spec_helper'
describe PagesDomain do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:pages_domain) { described_class.new }
+
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
@@ -64,19 +68,51 @@ describe PagesDomain do
end
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:verification_code) }
+ end
+
+ describe '#verification_code' do
+ subject { pages_domain.verification_code }
+
+ it 'is set automatically with 128 bits of SecureRandom data' do
+ expect(SecureRandom).to receive(:hex).with(16) { 'verification code' }
+
+ is_expected.to eq('verification code')
+ end
+ end
+
+ describe '#keyed_verification_code' do
+ subject { pages_domain.keyed_verification_code }
+
+ it { is_expected.to eq("gitlab-pages-verification-code=#{pages_domain.verification_code}") }
+ end
+
+ describe '#verification_domain' do
+ subject { pages_domain.verification_domain }
+
+ it { is_expected.to be_nil }
+
+ it 'is a well-known subdomain if the domain is present' do
+ pages_domain.domain = 'example.com'
+
+ is_expected.to eq('_gitlab-pages-verification-code.example.com')
+ end
+ end
+
describe '#url' do
subject { domain.url }
context 'without the certificate' do
let(:domain) { build(:pages_domain, certificate: '') }
- it { is_expected.to eq('http://my.domain.com') }
+ it { is_expected.to eq("http://#{domain.domain}") }
end
context 'with a certificate' do
let(:domain) { build(:pages_domain, :with_certificate) }
- it { is_expected.to eq('https://my.domain.com') }
+ it { is_expected.to eq("https://#{domain.domain}") }
end
end
@@ -154,4 +190,108 @@ describe PagesDomain do
# We test only existence of output, since the output is long
it { is_expected.not_to be_empty }
end
+
+ describe '#update_daemon' do
+ it 'runs when the domain is created' do
+ domain = build(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.save!
+ end
+
+ it 'runs when the domain is destroyed' do
+ domain = create(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.destroy!
+ end
+
+ it 'delegates to Projects::UpdatePagesConfigurationService' do
+ service = instance_double('Projects::UpdatePagesConfigurationService')
+ expect(Projects::UpdatePagesConfigurationService).to receive(:new) { service }
+ expect(service).to receive(:execute)
+
+ create(:pages_domain)
+ end
+
+ context 'configuration updates when attributes change' do
+ set(:project1) { create(:project) }
+ set(:project2) { create(:project) }
+ set(:domain) { create(:pages_domain) }
+
+ where(:attribute, :old_value, :new_value, :update_expected) do
+ now = Time.now
+ future = now + 1.day
+
+ :project | nil | :project1 | true
+ :project | :project1 | :project1 | false
+ :project | :project1 | :project2 | true
+ :project | :project1 | nil | true
+
+ # domain can't be set to nil
+ :domain | 'a.com' | 'a.com' | false
+ :domain | 'a.com' | 'b.com' | true
+
+ # verification_code can't be set to nil
+ :verification_code | 'foo' | 'foo' | false
+ :verification_code | 'foo' | 'bar' | false
+
+ :verified_at | nil | now | false
+ :verified_at | now | now | false
+ :verified_at | now | future | false
+ :verified_at | now | nil | false
+
+ :enabled_until | nil | now | true
+ :enabled_until | now | now | false
+ :enabled_until | now | future | false
+ :enabled_until | now | nil | true
+ end
+
+ with_them do
+ it 'runs if a relevant attribute has changed' do
+ a = old_value.is_a?(Symbol) ? send(old_value) : old_value
+ b = new_value.is_a?(Symbol) ? send(new_value) : new_value
+
+ domain.update!(attribute => a)
+
+ if update_expected
+ expect(domain).to receive(:update_daemon)
+ else
+ expect(domain).not_to receive(:update_daemon)
+ end
+
+ domain.update!(attribute => b)
+ end
+ end
+
+ context 'TLS configuration' do
+ set(:domain_with_tls) { create(:pages_domain, :with_key, :with_certificate) }
+
+ let(:cert1) { domain_with_tls.certificate }
+ let(:cert2) { cert1 + ' ' }
+ let(:key1) { domain_with_tls.key }
+ let(:key2) { key1 + ' ' }
+
+ it 'updates when added' do
+ expect(domain).to receive(:update_daemon)
+
+ domain.update!(key: key1, certificate: cert1)
+ end
+
+ it 'updates when changed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: key2, certificate: cert2)
+ end
+
+ it 'updates when removed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: nil, certificate: nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 836ffb7cea0..62fdf870090 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1678,6 +1678,78 @@ describe NotificationService, :mailer do
end
end
+ describe 'Pages domains' do
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:u_blocked) { create(:user, :blocked) }
+ set(:u_silence) { create_user_with_notification(:disabled, 'silent', project) }
+ set(:u_owner) { project.owner }
+ set(:u_master1) { create(:user) }
+ set(:u_master2) { create(:user) }
+ set(:u_developer) { create(:user) }
+
+ before do
+ project.add_master(u_blocked)
+ project.add_master(u_silence)
+ project.add_master(u_master1)
+ project.add_master(u_master2)
+ project.add_developer(u_developer)
+
+ reset_delivered_emails!
+ end
+
+ %i[
+ pages_domain_enabled
+ pages_domain_disabled
+ pages_domain_verification_succeeded
+ pages_domain_verification_failed
+ ].each do |sym|
+ describe "##{sym}" do
+ subject(:notify!) { notification.send(sym, domain) }
+
+ it 'emails current watching masters' do
+ expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
+
+ notify!
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+
+ it 'emails nobody if the project is missing' do
+ domain.project = nil
+
+ notify!
+
+ should_not_email_anyone
+ end
+ end
+ end
+
+ describe '#pages_domain_verification_failed' do
+ it 'emails current watching masters' do
+ notification.pages_domain_verification_failed(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_enabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_enabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_disabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_disabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/verify_pages_domain_service_spec.rb b/spec/services/verify_pages_domain_service_spec.rb
new file mode 100644
index 00000000000..576db1dde2d
--- /dev/null
+++ b/spec/services/verify_pages_domain_service_spec.rb
@@ -0,0 +1,270 @@
+require 'spec_helper'
+
+describe VerifyPagesDomainService do
+ using RSpec::Parameterized::TableSyntax
+ include EmailHelpers
+
+ let(:error_status) { { status: :error, message: "Couldn't verify #{domain.domain}" } }
+
+ subject(:service) { described_class.new(domain) }
+
+ describe '#execute' do
+ context 'verification code recognition (verified domain)' do
+ where(:domain_sym, :code_sym) do
+ :domain | :verification_code
+ :domain | :keyed_verification_code
+
+ :verification_domain | :verification_code
+ :verification_domain | :keyed_verification_code
+ end
+
+ with_them do
+ set(:domain) { create(:pages_domain) }
+
+ let(:domain_name) { domain.send(domain_sym) }
+ let(:verification_code) { domain.send(code_sym) }
+
+ it 'verifies and enables the domain' do
+ stub_resolver(domain_name => ['something else', verification_code])
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'verifies and enables when the code is contained partway through a TXT record' do
+ stub_resolver(domain_name => "something #{verification_code} else")
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not verify when the code is not present' do
+ stub_resolver(domain_name => 'something else')
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'verified domain' do
+ set(:domain) { create(:pages_domain) }
+
+ it 'unverifies (but does not disable) when the right code is not present' do
+ stub_resolver(domain.domain => 'something else')
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'unverifies (but does not disable) when no records are present' do
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'expired domain' do
+ set(:domain) { create(:pages_domain, :expired) }
+
+ it 'verifies and enables when the right code is present' do
+ stub_resolver(domain.domain => domain.keyed_verification_code)
+
+ expect(service.execute).to eq(status: :success)
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'disables when the right code is not present' do
+ error_status[:message] += '. It is now disabled.'
+
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).not_to be_enabled
+ end
+ end
+ end
+
+ context 'timeout behaviour' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'sets a timeout on the DNS query' do
+ expect(stub_resolver).to receive(:timeouts=).with(described_class::RESOLVER_TIMEOUT_SECONDS)
+
+ service.execute
+ end
+ end
+
+ context 'email notifications' do
+ let(:notification_service) { instance_double('NotificationService') }
+
+ where(:factory, :verification_succeeds, :expected_notification) do
+ nil | true | nil
+ nil | false | :verification_failed
+ :reverify | true | nil
+ :reverify | false | :verification_failed
+ :unverified | true | :verification_succeeded
+ :unverified | false | nil
+ :expired | true | nil
+ :expired | false | :disabled
+ :disabled | true | :enabled
+ :disabled | false | nil
+ end
+
+ with_them do
+ let(:domain) { create(:pages_domain, *[factory].compact) }
+
+ before do
+ allow(service).to receive(:notification_service) { notification_service }
+
+ if verification_succeeds
+ stub_resolver(domain.domain => domain.verification_code)
+ else
+ stub_resolver
+ end
+ end
+
+ it 'sends a notification if appropriate' do
+ if expected_notification
+ expect(notification_service).to receive(:"pages_domain_#{expected_notification}").with(domain)
+ end
+
+ service.execute
+ end
+ end
+
+ context 'pages verification disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ allow(service).to receive(:notification_service) { notification_service }
+ end
+
+ it 'skips email notifications' do
+ expect(notification_service).not_to receive(:pages_domain_enabled)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'pages configuration updates' do
+ context 'enabling a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'verifying an enabled domain' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'disabling an expired domain' do
+ let(:domain) { create(:pages_domain, :expired) }
+
+ it 'schedules an update' do
+ stub_resolver
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'failing to verify a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'does not schedule an update' do
+ stub_resolver
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'no verification code' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'returns an error' do
+ domain.verification_code = ''
+
+ disallow_resolver!
+
+ expect(service.execute).to eq(status: :error, message: "No verification code set for #{domain.domain}")
+ end
+ end
+
+ context 'pages domain verification is disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ end
+
+ it 'extends domain validity by unconditionally reverifying' do
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not shorten any grace period' do
+ grace = Time.now + 1.year
+ domain.update!(enabled_until: grace)
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain.enabled_until).to be_like_time(grace)
+ end
+ end
+ end
+
+ def disallow_resolver!
+ expect(Resolv::DNS).not_to receive(:open)
+ end
+
+ def stub_resolver(stubbed_lookups = {})
+ resolver = instance_double('Resolv::DNS')
+ allow(resolver).to receive(:timeouts=)
+
+ expect(Resolv::DNS).to receive(:open).and_yield(resolver)
+
+ allow(resolver).to receive(:getresources) { [] }
+ stubbed_lookups.each do |domain, records|
+ records = Array(records).map { |txt| Resolv::DNS::Resource::IN::TXT.new(txt) }
+ allow(resolver).to receive(:getresources).with(domain, Resolv::DNS::Resource::IN::TXT) { records }
+ end
+
+ resolver
+ end
+end
diff --git a/spec/workers/pages_domain_verification_cron_worker_spec.rb b/spec/workers/pages_domain_verification_cron_worker_spec.rb
new file mode 100644
index 00000000000..8f780428c82
--- /dev/null
+++ b/spec/workers/pages_domain_verification_cron_worker_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationCronWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#perform' do
+ it 'enqueues a PagesDomainVerificationWorker for domains needing verification' do
+ verified = create(:pages_domain)
+ reverify = create(:pages_domain, :reverify)
+ disabled = create(:pages_domain, :disabled)
+
+ [reverify, disabled].each do |domain|
+ expect(PagesDomainVerificationWorker).to receive(:perform_async).with(domain.id)
+ end
+
+ expect(PagesDomainVerificationWorker).not_to receive(:perform_async).with(verified.id)
+
+ worker.perform
+ end
+ end
+end
diff --git a/spec/workers/pages_domain_verification_worker_spec.rb b/spec/workers/pages_domain_verification_worker_spec.rb
new file mode 100644
index 00000000000..372fc95ab4a
--- /dev/null
+++ b/spec/workers/pages_domain_verification_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationWorker do
+ subject(:worker) { described_class.new }
+
+ let(:domain) { create(:pages_domain) }
+
+ describe '#perform' do
+ it 'does nothing for a non-existent domain' do
+ domain.destroy
+
+ expect(VerifyPagesDomainService).not_to receive(:new)
+
+ expect { worker.perform(domain.id) }.not_to raise_error
+ end
+
+ it 'delegates to VerifyPagesDomainService' do
+ service = double(:service)
+ expected_domain = satisfy { |obj| obj == domain }
+
+ expect(VerifyPagesDomainService).to receive(:new).with(expected_domain) { service }
+ expect(service).to receive(:execute)
+
+ worker.perform(domain.id)
+ end
+ end
+end