dns_mock 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +89 -0
  3. data/.codeclimate.yml +13 -0
  4. data/.github/BRANCH_NAMING_CONVENTION.md +36 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
  6. data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  7. data/.github/ISSUE_TEMPLATE/issue_report.md +28 -0
  8. data/.github/ISSUE_TEMPLATE/question.md +22 -0
  9. data/.github/PULL_REQUEST_TEMPLATE.md +49 -0
  10. data/.gitignore +10 -0
  11. data/.overcommit.yml +32 -0
  12. data/.reek.yml +46 -0
  13. data/.rspec +2 -0
  14. data/.rubocop.yml +273 -0
  15. data/.ruby-gemset +1 -0
  16. data/.ruby-version +1 -0
  17. data/CHANGELOG.md +9 -0
  18. data/CODE_OF_CONDUCT.md +74 -0
  19. data/CONTRIBUTING.md +46 -0
  20. data/Gemfile +5 -0
  21. data/Gemfile.lock +127 -0
  22. data/LICENSE.txt +21 -0
  23. data/README.md +79 -0
  24. data/Rakefile +8 -0
  25. data/bin/console +15 -0
  26. data/bin/setup +8 -0
  27. data/dns_mock.gemspec +48 -0
  28. data/lib/dns_mock.rb +19 -0
  29. data/lib/dns_mock/core.rb +56 -0
  30. data/lib/dns_mock/error/argument_type.rb +11 -0
  31. data/lib/dns_mock/error/helper.rb +22 -0
  32. data/lib/dns_mock/error/port_in_use.rb +11 -0
  33. data/lib/dns_mock/error/random_free_port.rb +11 -0
  34. data/lib/dns_mock/error/record_context.rb +11 -0
  35. data/lib/dns_mock/error/record_context_type.rb +15 -0
  36. data/lib/dns_mock/error/record_host_type.rb +11 -0
  37. data/lib/dns_mock/error/record_not_found.rb +11 -0
  38. data/lib/dns_mock/error/record_type.rb +11 -0
  39. data/lib/dns_mock/record/builder/a.rb +9 -0
  40. data/lib/dns_mock/record/builder/aaaa.rb +9 -0
  41. data/lib/dns_mock/record/builder/base.rb +26 -0
  42. data/lib/dns_mock/record/builder/cname.rb +13 -0
  43. data/lib/dns_mock/record/builder/mx.rb +22 -0
  44. data/lib/dns_mock/record/builder/ns.rb +9 -0
  45. data/lib/dns_mock/record/builder/soa.rb +19 -0
  46. data/lib/dns_mock/record/builder/txt.rb +9 -0
  47. data/lib/dns_mock/record/factory/a.rb +15 -0
  48. data/lib/dns_mock/record/factory/aaaa.rb +15 -0
  49. data/lib/dns_mock/record/factory/base.rb +54 -0
  50. data/lib/dns_mock/record/factory/cname.rb +15 -0
  51. data/lib/dns_mock/record/factory/mx.rb +15 -0
  52. data/lib/dns_mock/record/factory/ns.rb +15 -0
  53. data/lib/dns_mock/record/factory/soa.rb +15 -0
  54. data/lib/dns_mock/record/factory/txt.rb +15 -0
  55. data/lib/dns_mock/response/answer.rb +32 -0
  56. data/lib/dns_mock/response/message.rb +29 -0
  57. data/lib/dns_mock/server.rb +82 -0
  58. data/lib/dns_mock/server/random_available_port.rb +48 -0
  59. data/lib/dns_mock/server/records_dictionary_builder.rb +52 -0
  60. data/lib/dns_mock/version.rb +5 -0
  61. metadata +334 -0
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'dns_mock/core'
4
+
5
+ module DnsMock
6
+ class << self
7
+ def start_server(server = DnsMock::Server, records: {}, port: nil)
8
+ server.new(records: records, port: port)
9
+ end
10
+
11
+ def running_servers
12
+ ::ObjectSpace.each_object(DnsMock::Server).select(&:alive?)
13
+ end
14
+
15
+ def stop_running_servers!
16
+ running_servers.all?(&:stop!)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'resolv'
4
+ require 'socket'
5
+
6
+ module DnsMock
7
+ AVAILABLE_DNS_RECORD_TYPES = %i[a aaaa cname mx ns soa txt].freeze
8
+
9
+ module Error
10
+ require_relative '../dns_mock/error/argument_type'
11
+ require_relative '../dns_mock/error/port_in_use'
12
+ require_relative '../dns_mock/error/random_free_port'
13
+ require_relative '../dns_mock/error/record_context_type'
14
+ require_relative '../dns_mock/error/record_context'
15
+ require_relative '../dns_mock/error/record_host_type'
16
+ require_relative '../dns_mock/error/record_not_found'
17
+ require_relative '../dns_mock/error/record_type'
18
+ require_relative '../dns_mock/error/helper'
19
+ end
20
+
21
+ module Record
22
+ module Factory
23
+ require_relative '../dns_mock/record/factory/base'
24
+ require_relative '../dns_mock/record/factory/a'
25
+ require_relative '../dns_mock/record/factory/aaaa'
26
+ require_relative '../dns_mock/record/factory/cname'
27
+ require_relative '../dns_mock/record/factory/mx'
28
+ require_relative '../dns_mock/record/factory/ns'
29
+ require_relative '../dns_mock/record/factory/soa'
30
+ require_relative '../dns_mock/record/factory/txt'
31
+ end
32
+ end
33
+
34
+ module Record
35
+ module Builder
36
+ require_relative '../dns_mock/record/builder/base'
37
+ require_relative '../dns_mock/record/builder/a'
38
+ require_relative '../dns_mock/record/builder/aaaa'
39
+ require_relative '../dns_mock/record/builder/cname'
40
+ require_relative '../dns_mock/record/builder/mx'
41
+ require_relative '../dns_mock/record/builder/ns'
42
+ require_relative '../dns_mock/record/builder/soa'
43
+ require_relative '../dns_mock/record/builder/txt'
44
+ end
45
+ end
46
+
47
+ module Response
48
+ require_relative '../dns_mock/response/answer'
49
+ require_relative '../dns_mock/response/message'
50
+ end
51
+
52
+ require_relative '../dns_mock/version'
53
+ require_relative '../dns_mock/server/records_dictionary_builder'
54
+ require_relative '../dns_mock/server/random_available_port'
55
+ require_relative '../dns_mock/server'
56
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ ArgumentType = ::Class.new(::ArgumentError) do
6
+ def initialize(argument_class)
7
+ super("Argument class is a #{argument_class}. Should be a Hash")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ module Helper
6
+ def raise_record_context_type_error(record_type, record_context, expected_type)
7
+ current_type, record_type = record_context.class, record_type.upcase
8
+ raise_unless(DnsMock::Error::RecordContextType.new(current_type, record_type, expected_type), current_type.eql?(expected_type))
9
+ end
10
+
11
+ def raise_record_type_error(record_type, condition)
12
+ raise_unless(DnsMock::Error::RecordType.new(record_type), condition)
13
+ end
14
+
15
+ private
16
+
17
+ def raise_unless(error_instance, condition)
18
+ raise error_instance unless condition
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ PortInUse = ::Class.new(::RuntimeError) do
6
+ def initialize(hostname, port)
7
+ super("Impossible to bind UDP DNS mock server on #{hostname}:#{port}. Address already in use")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RandomFreePort = ::Class.new(::RuntimeError) do
6
+ def initialize(attempts)
7
+ super("Impossible to find free random port in #{attempts} attempts")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RecordContext = ::Class.new(::StandardError) do
6
+ def initialize(record_context, record_type)
7
+ super("#{record_context}. Invalid #{record_type} record context")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RecordContextType = ::Class.new(::ArgumentError) do
6
+ def initialize(record_context_type, record_type, expected_record_context_type)
7
+ super(
8
+ "#{record_context_type} is invalid record context type for " \
9
+ "#{record_type} record. Should be a " \
10
+ "#{expected_record_context_type}"
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RecordHostType = ::Class.new(::ArgumentError) do
6
+ def initialize(hostname, hostname_class)
7
+ super("Hostname #{hostname} type is #{hostname_class}. Should be a String")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RecordNotFound = ::Class.new(::StandardError) do
6
+ def initialize(record_type, hostname)
7
+ super("#{record_type.upcase} record not found for #{hostname} in predefined records dictionary")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Error
5
+ RecordType = ::Class.new(::StandardError) do
6
+ def initialize(record_type)
7
+ super("#{record_type} is invalid record type")
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ A = ::Class.new(DnsMock::Record::Builder::Base)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ Aaaa = ::Class.new(DnsMock::Record::Builder::Base)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ class Base
7
+ def self.call(target_factory, records_data)
8
+ new(target_factory, records_data).build
9
+ end
10
+
11
+ def initialize(target_factory, records_data)
12
+ @target_factory = target_factory
13
+ @records_data = records_data
14
+ end
15
+
16
+ def build
17
+ records_data.map { |record_data| target_factory.new(record_data: record_data).create }
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :target_factory, :records_data
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ Cname = ::Class.new(DnsMock::Record::Builder::Base) do
7
+ def build
8
+ [target_factory.new(record_data: records_data).create]
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ class Mx < DnsMock::Record::Builder::Base
7
+ RECORD_PREFERENCE_STEP = 10
8
+
9
+ def build
10
+ records_data.map.with_index(1) do |record_data, record_preference|
11
+ target_factory.new(
12
+ record_data: [
13
+ record_preference * DnsMock::Record::Builder::Mx::RECORD_PREFERENCE_STEP,
14
+ record_data
15
+ ]
16
+ ).create
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ Ns = ::Class.new(DnsMock::Record::Builder::Base)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ class Soa < DnsMock::Record::Builder::Base
7
+ FACTORY_ARGS_ORDER = %i[mname rname serial refresh retry expire minimum].freeze
8
+
9
+ def build
10
+ records_data.map do |record_data|
11
+ target_factory.new(
12
+ record_data: record_data.values_at(*DnsMock::Record::Builder::Soa::FACTORY_ARGS_ORDER)
13
+ ).create
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Builder
6
+ Txt = ::Class.new(DnsMock::Record::Builder::Base)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Factory
6
+ A = ::Class.new(DnsMock::Record::Factory::Base) do
7
+ record_type :a
8
+
9
+ def instance_params
10
+ record_data
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Factory
6
+ Aaaa = ::Class.new(DnsMock::Record::Factory::Base) do
7
+ record_type :aaaa
8
+
9
+ def instance_params
10
+ record_data
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Factory
6
+ class Base
7
+ extend DnsMock::Error::Helper
8
+
9
+ class << self
10
+ attr_reader :target_class
11
+
12
+ def record_type(record_type)
13
+ @target_class = ::Resolv::DNS::Resource::IN.const_get(
14
+ record_type_check(record_type).upcase
15
+ )
16
+ end
17
+
18
+ private
19
+
20
+ def record_type_check(defined_record_type)
21
+ raise_record_type_error(defined_record_type, DnsMock::AVAILABLE_DNS_RECORD_TYPES.include?(defined_record_type))
22
+ defined_record_type
23
+ end
24
+ end
25
+
26
+ def initialize(dns_name = ::Resolv::DNS::Name, record_data:)
27
+ @dns_name = dns_name
28
+ @record_data = record_data
29
+ end
30
+
31
+ def instance_params; end
32
+
33
+ def create
34
+ self.class.target_class.public_send(:new, *instance_params)
35
+ rescue => error
36
+ raise DnsMock::Error::RecordContext.new(error.message, record_type)
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :dns_name, :record_data
42
+
43
+ def record_type
44
+ self.class.name.split('::').last.upcase
45
+ end
46
+
47
+ def create_dns_name(hostname)
48
+ raise ::ArgumentError, "cannot interpret as DNS name: #{hostname}" unless hostname.is_a?(::String)
49
+ dns_name.create("#{hostname}.")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Factory
6
+ Cname = ::Class.new(DnsMock::Record::Factory::Base) do
7
+ record_type :cname
8
+
9
+ def instance_params
10
+ [create_dns_name(record_data)]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DnsMock
4
+ module Record
5
+ module Factory
6
+ Mx = ::Class.new(DnsMock::Record::Factory::Base) do
7
+ record_type :mx
8
+
9
+ def instance_params
10
+ [record_data.first, create_dns_name(record_data.last)]
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end