dns_mock 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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