مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2025-11-30 21:32:30 +00:00
refactor: refactor DNS resolution
This commit also adds some of tests for the Domain model. It was during the writing of these tests that the DNS resolution refactoring requirement became apparent.
هذا الالتزام موجود في:
317
spec/models/domain_spec.rb
Normal file
317
spec/models/domain_spec.rb
Normal file
@@ -0,0 +1,317 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
describe Domain do
|
||||
subject(:domain) { build(:domain) }
|
||||
|
||||
describe "relationships" do
|
||||
it { is_expected.to belong_to(:server).optional }
|
||||
it { is_expected.to belong_to(:owner).optional }
|
||||
it { is_expected.to have_many(:routes) }
|
||||
it { is_expected.to have_many(:track_domains) }
|
||||
end
|
||||
|
||||
describe "validations" do
|
||||
it { is_expected.to validate_presence_of(:name) }
|
||||
it { is_expected.to validate_uniqueness_of(:name).scoped_to([:owner_type, :owner_id]).case_insensitive.with_message("is already added") }
|
||||
it { is_expected.to allow_value("example.com").for(:name) }
|
||||
it { is_expected.to allow_value("example.co.uk").for(:name) }
|
||||
it { is_expected.to_not allow_value("EXAMPLE.COM").for(:name) }
|
||||
it { is_expected.to_not allow_value("example.com ").for(:name) }
|
||||
it { is_expected.to_not allow_value("example com").for(:name) }
|
||||
it { is_expected.to validate_inclusion_of(:verification_method).in_array(Domain::VERIFICATION_METHODS) }
|
||||
end
|
||||
|
||||
describe "creation" do
|
||||
it "creates a new dkim identifier string" do
|
||||
expect { domain.save }.to change { domain.dkim_identifier_string }.from(nil).to(match(/\A[a-zA-Z0-9]{6}\z/))
|
||||
end
|
||||
|
||||
it "generates a new dkim key" do
|
||||
expect { domain.save }.to change { domain.dkim_private_key }.from(nil).to(match(/\A-+BEGIN RSA PRIVATE KEY-+/))
|
||||
end
|
||||
|
||||
it "generates a UUID" do
|
||||
expect { domain.save }.to change { domain.uuid }.from(nil).to(/[a-f0-9-]{36}/)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".verified" do
|
||||
it "returns verified domains only" do
|
||||
verified_domain = create(:domain)
|
||||
create(:domain, :unverified)
|
||||
expect(described_class.verified).to eq [verified_domain]
|
||||
end
|
||||
end
|
||||
|
||||
context "when verification method changes" do
|
||||
context "to DNS" do
|
||||
let(:domain) { create(:domain, :unverified, verification_method: "Email") }
|
||||
|
||||
it "generates a DNS suitable verification token" do
|
||||
domain.verification_method = "DNS"
|
||||
expect { domain.save }.to change { domain.verification_token }.from(match(/\A\d{6}\z/)).to(match(/\A[A-Za-z0-9+]{32}\z/))
|
||||
end
|
||||
end
|
||||
|
||||
context "to Email" do
|
||||
let(:domain) { create(:domain, :unverified, verification_method: "DNS") }
|
||||
|
||||
it "generates an email suitable verification token" do
|
||||
domain.verification_method = "Email"
|
||||
expect { domain.save }.to change { domain.verification_token }.from(match(/\A[A-Za-z0-9+]{32}\z/)).to(match(/\A\d{6}\z/))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#verified?" do
|
||||
context "when the domain is verified" do
|
||||
it "returns true" do
|
||||
expect(domain.verified?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context "when the domain is not verified" do
|
||||
let(:domain) { build(:domain, :unverified) }
|
||||
|
||||
it "returns false" do
|
||||
expect(domain.verified?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#mark_as_verified" do
|
||||
context "when already verified" do
|
||||
it "returns false" do
|
||||
expect(domain.mark_as_verified).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when unverified" do
|
||||
let(:domain) { create(:domain, :unverified) }
|
||||
|
||||
it "sets the verification time" do
|
||||
expect { domain.mark_as_verified }.to change { domain.verified_at }.from(nil).to(kind_of(Time))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#parent_domains" do
|
||||
context "at level 1" do
|
||||
let(:domain) { build(:domain, name: "example.com") }
|
||||
|
||||
it "returns the current domain only" do
|
||||
expect(domain.parent_domains).to eq ["example.com"]
|
||||
end
|
||||
end
|
||||
|
||||
context "at level 2" do
|
||||
let(:domain) { build(:domain, name: "test.example.com") }
|
||||
|
||||
it "returns the current domain plus its parent" do
|
||||
expect(domain.parent_domains).to eq ["test.example.com", "example.com"]
|
||||
end
|
||||
end
|
||||
|
||||
context "at level 3 (and higher)" do
|
||||
let(:domain) { build(:domain, name: "sub.test.example.com") }
|
||||
|
||||
it "returns the current domain plus its parents" do
|
||||
expect(domain.parent_domains).to eq ["sub.test.example.com", "test.example.com", "example.com"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#generate_dkim_key" do
|
||||
it "generates a new dkim key" do
|
||||
expect { domain.generate_dkim_key }.to change { domain.dkim_private_key }.from(nil).to(match(/\A-+BEGIN RSA PRIVATE KEY-+/))
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dkim_key" do
|
||||
context "when the domain has a DKIM key" do
|
||||
let(:domain) { create(:domain) }
|
||||
|
||||
it "returns the dkim key as a OpenSSL::PKey::RSA" do
|
||||
expect(domain.dkim_key).to be_a OpenSSL::PKey::RSA
|
||||
expect(domain.dkim_key.to_s).to eq domain.dkim_private_key
|
||||
end
|
||||
end
|
||||
|
||||
context "when the domain has no DKIM key" do
|
||||
let(:domain) { build(:domain) }
|
||||
|
||||
it "returns nil" do
|
||||
expect(domain.dkim_key).to be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#to_param" do
|
||||
context "when the domain has not been saved" do
|
||||
it "returns nil" do
|
||||
expect(domain.to_param).to be_nil
|
||||
end
|
||||
end
|
||||
context "when the domain has been saved" do
|
||||
before do
|
||||
domain.save
|
||||
end
|
||||
|
||||
it "returns the UUID" do
|
||||
expect(domain.to_param).to eq domain.uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#verification_email_addresses" do
|
||||
let(:domain) { build(:domain, name: "example.com") }
|
||||
|
||||
it "returns the verification email addresses" do
|
||||
expect(domain.verification_email_addresses).to eq [
|
||||
"webmaster@example.com",
|
||||
"postmaster@example.com",
|
||||
"admin@example.com",
|
||||
"administrator@example.com",
|
||||
"hostmaster@example.com"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#spf_record" do
|
||||
it "returns the SPF record" do
|
||||
expect(domain.spf_record).to eq "v=spf1 a mx include:#{Postal.config.dns.spf_include} ~all"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dkim_record" do
|
||||
context "when the domain has no DKIM key" do
|
||||
it "returns nil" do
|
||||
expect(domain.dkim_record).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the domain has a DKIM key" do
|
||||
before do
|
||||
domain.save
|
||||
end
|
||||
|
||||
it "returns the DKIM record" do
|
||||
expect(domain.dkim_record).to match(/\Av=DKIM1; t=s; h=sha256; p=.*;\z/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dkim_identifier" do
|
||||
context "when the domain has no dkim identifier string" do
|
||||
it "returns nil" do
|
||||
expect(domain.dkim_identifier).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the domain has a dkim identifier string" do
|
||||
before do
|
||||
domain.save
|
||||
end
|
||||
|
||||
it "returns the DKIM identifier" do
|
||||
expect(domain.dkim_identifier).to eq "#{Postal.config.dns.dkim_identifier}-#{domain.dkim_identifier_string}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dkim_record_name" do
|
||||
context "when the domain has no dkim identifier string" do
|
||||
it "returns nil" do
|
||||
expect(domain.dkim_record_name).to be_nil
|
||||
end
|
||||
end
|
||||
|
||||
context "when the domain has a dkim identifier string" do
|
||||
before do
|
||||
domain.save
|
||||
end
|
||||
|
||||
it "returns the DKIM identifier" do
|
||||
expect(domain.dkim_record_name).to eq "#{Postal.config.dns.dkim_identifier}-#{domain.dkim_identifier_string}._domainkey"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#return_path_domain" do
|
||||
it "returns the return path domain" do
|
||||
expect(domain.return_path_domain).to eq "#{Postal.config.dns.custom_return_path_prefix}.#{domain.name}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#dns_verification_string" do
|
||||
let(:domain) { create(:domain, verification_method: "DNS") }
|
||||
|
||||
it "returns the DNS verification string" do
|
||||
expect(domain.dns_verification_string).to eq "#{Postal.config.dns.domain_verify_prefix} #{domain.verification_token}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#resolver" do
|
||||
context "when the local nameservers should be used" do
|
||||
before do
|
||||
allow(Postal.config.general).to receive(:use_local_ns_for_domains?).and_return(true)
|
||||
end
|
||||
|
||||
it "uses the local DNS" do
|
||||
expect(domain.resolver).to eq DNSResolver.local
|
||||
end
|
||||
end
|
||||
|
||||
context "when local nameservers should not be used" do
|
||||
it "uses the a resolver for this domain" do
|
||||
allow(DNSResolver).to receive(:for_domain).with(domain.name).and_return(DNSResolver.new(nameservers: ["1.2.3.4"]))
|
||||
expect(domain.resolver).to be_a DNSResolver
|
||||
expect(domain.resolver.nameservers).to eq ["1.2.3.4"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#verify_with_dns" do
|
||||
context "when the verification method is not DNS" do
|
||||
let(:domain) { build(:domain, verification_method: "Email") }
|
||||
|
||||
it "returns false" do
|
||||
expect(domain.verify_with_dns).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context "when a TXT record is found that matches" do
|
||||
let(:domain) { create(:domain, :unverified) }
|
||||
|
||||
before do
|
||||
allow(domain.resolver).to receive(:txt).with(domain.name).and_return([domain.dns_verification_string])
|
||||
end
|
||||
|
||||
it "returns true" do
|
||||
expect(domain.verify_with_dns).to be true
|
||||
end
|
||||
|
||||
it "sets the verification time" do
|
||||
expect { domain.verify_with_dns }.to change { domain.verified_at }.from(nil).to(kind_of(Time))
|
||||
end
|
||||
end
|
||||
|
||||
context "when no TXT record is found" do
|
||||
let(:domain) { create(:domain, :unverified) }
|
||||
|
||||
before do
|
||||
allow(domain.resolver).to receive(:txt).with(domain.name).and_return(["something", "something else"])
|
||||
end
|
||||
|
||||
it "returns false" do
|
||||
expect(domain.verify_with_dns).to be false
|
||||
end
|
||||
|
||||
it "does not set the verification time" do
|
||||
expect { domain.verify_with_dns }.to_not change { domain.verified_at } # rubocop:disable Lint/AmbiguousBlockAssociation
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
المرجع في مشكلة جديدة
حظر مستخدم