From a9e91eb95565a7da74d7c961e56e9c7d6d6b94b1 Mon Sep 17 00:00:00 2001
From: Matt Jankowski <matt@jankowski.online>
Date: Thu, 8 Feb 2024 09:26:45 -0500
Subject: [PATCH] Add common stub setup for resolv dns in email mx validator
 spec (#29140)

---
 spec/validators/email_mx_validator_spec.rb | 94 +++++++++++-----------
 1 file changed, 45 insertions(+), 49 deletions(-)

diff --git a/spec/validators/email_mx_validator_spec.rb b/spec/validators/email_mx_validator_spec.rb
index 21b1ad0a1..bc26be872 100644
--- a/spec/validators/email_mx_validator_spec.rb
+++ b/spec/validators/email_mx_validator_spec.rb
@@ -5,6 +5,7 @@ require 'rails_helper'
 describe EmailMxValidator do
   describe '#validate' do
     let(:user) { instance_double(User, email: 'foo@example.com', sign_up_ip: '1.2.3.4', errors: instance_double(ActiveModel::Errors, add: nil)) }
+    let(:resolv_dns_double) { instance_double(Resolv::DNS) }
 
     context 'with an e-mail domain that is explicitly allowed' do
       around do |block|
@@ -15,13 +16,7 @@ describe EmailMxValidator do
       end
 
       it 'does not add errors if there are no DNS records' do
-        resolver = instance_double(Resolv::DNS)
-
-        allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
-        allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
-        allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-        allow(resolver).to receive(:timeouts=).and_return(nil)
-        allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+        configure_resolver('example.com')
 
         subject.validate(user)
         expect(user.errors).to_not have_received(:add)
@@ -29,13 +24,7 @@ describe EmailMxValidator do
     end
 
     it 'adds no error if there are DNS records for the e-mail domain' do
-      resolver = instance_double(Resolv::DNS)
-
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:timeouts=).and_return(nil)
-      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+      configure_resolver('example.com', a: resolv_double_a('192.0.2.42'))
 
       subject.validate(user)
       expect(user.errors).to_not have_received(:add)
@@ -58,13 +47,7 @@ describe EmailMxValidator do
     end
 
     it 'adds an error if the email domain name contains empty labels' do
-      resolver = instance_double(Resolv::DNS)
-
-      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::MX).and_return([])
-      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::A).and_return([Resolv::DNS::Resource::IN::A.new('192.0.2.42')])
-      allow(resolver).to receive(:getresources).with('example..com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:timeouts=).and_return(nil)
-      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+      configure_resolver('example..com', a: resolv_double_a('192.0.2.42'))
 
       user = instance_double(User, email: 'foo@example..com', sign_up_ip: '1.2.3.4', errors: instance_double(ActiveModel::Errors, add: nil))
       subject.validate(user)
@@ -72,30 +55,15 @@ describe EmailMxValidator do
     end
 
     it 'adds an error if there are no DNS records for the e-mail domain' do
-      resolver = instance_double(Resolv::DNS)
-
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:timeouts=).and_return(nil)
-      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+      configure_resolver('example.com')
 
       subject.validate(user)
       expect(user.errors).to have_received(:add)
     end
 
     it 'adds an error if a MX record does not lead to an IP' do
-      resolver = instance_double(Resolv::DNS)
-
-      allow(resolver).to receive(:getresources)
-        .with('example.com', Resolv::DNS::Resource::IN::MX)
-        .and_return([instance_double(Resolv::DNS::Resource::MX, exchange: 'mail.example.com')])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
-      allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:timeouts=).and_return(nil)
-      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+      configure_resolver('example.com', mx: resolv_double_mx('mail.example.com'))
+      configure_resolver('mail.example.com')
 
       subject.validate(user)
       expect(user.errors).to have_received(:add)
@@ -103,20 +71,48 @@ describe EmailMxValidator do
 
     it 'adds an error if the MX record is blacklisted' do
       EmailDomainBlock.create!(domain: 'mail.example.com')
-      resolver = instance_double(Resolv::DNS)
 
-      allow(resolver).to receive(:getresources)
-        .with('example.com', Resolv::DNS::Resource::IN::MX)
-        .and_return([instance_double(Resolv::DNS::Resource::MX, exchange: 'mail.example.com')])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
-      allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
-      allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([instance_double(Resolv::DNS::Resource::IN::A, address: '2.3.4.5')])
-      allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([instance_double(Resolv::DNS::Resource::IN::AAAA, address: 'fd00::2')])
-      allow(resolver).to receive(:timeouts=).and_return(nil)
-      allow(Resolv::DNS).to receive(:open).and_yield(resolver)
+      configure_resolver(
+        'example.com',
+        mx: resolv_double_mx('mail.example.com')
+      )
+      configure_resolver(
+        'mail.example.com',
+        a: instance_double(Resolv::DNS::Resource::IN::A, address: '2.3.4.5'),
+        aaaa: instance_double(Resolv::DNS::Resource::IN::AAAA, address: 'fd00::2')
+      )
 
       subject.validate(user)
       expect(user.errors).to have_received(:add)
     end
   end
+
+  def configure_resolver(domain, options = {})
+    allow(resolv_dns_double)
+      .to receive(:getresources)
+      .with(domain, Resolv::DNS::Resource::IN::MX)
+      .and_return(Array(options[:mx]))
+    allow(resolv_dns_double)
+      .to receive(:getresources)
+      .with(domain, Resolv::DNS::Resource::IN::A)
+      .and_return(Array(options[:a]))
+    allow(resolv_dns_double)
+      .to receive(:getresources)
+      .with(domain, Resolv::DNS::Resource::IN::AAAA)
+      .and_return(Array(options[:aaaa]))
+    allow(resolv_dns_double)
+      .to receive(:timeouts=)
+      .and_return(nil)
+    allow(Resolv::DNS)
+      .to receive(:open)
+      .and_yield(resolv_dns_double)
+  end
+
+  def resolv_double_mx(domain)
+    instance_double(Resolv::DNS::Resource::MX, exchange: domain)
+  end
+
+  def resolv_double_a(domain)
+    Resolv::DNS::Resource::IN::A.new(domain)
+  end
 end