api-tester 1.0.0 → 1.1.3

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 (53) hide show
  1. checksums.yaml +5 -5
  2. data/.github/dependabot.yml +15 -0
  3. data/.github/workflows/dependabot.yml +29 -0
  4. data/.github/workflows/push.yml +39 -0
  5. data/.github/workflows/test.yml +31 -0
  6. data/.rubocop.yml +171 -0
  7. data/Gemfile +2 -0
  8. data/Guardfile +70 -0
  9. data/README.md +65 -61
  10. data/Rakefile +8 -3
  11. data/api-tester.gemspec +28 -24
  12. data/changelog.txt +10 -0
  13. data/lib/api-tester/config.rb +17 -15
  14. data/lib/api-tester/definition/boundary_case.rb +5 -4
  15. data/lib/api-tester/definition/contract.rb +10 -5
  16. data/lib/api-tester/definition/endpoint.rb +58 -32
  17. data/lib/api-tester/definition/fields/array_field.rb +26 -21
  18. data/lib/api-tester/definition/fields/boolean_field.rb +17 -7
  19. data/lib/api-tester/definition/fields/email_field.rb +28 -11
  20. data/lib/api-tester/definition/fields/enum_field.rb +19 -12
  21. data/lib/api-tester/definition/fields/field.rb +52 -45
  22. data/lib/api-tester/definition/fields/number_field.rb +20 -6
  23. data/lib/api-tester/definition/fields/object_field.rb +37 -30
  24. data/lib/api-tester/definition/fields/plain_array_field.rb +25 -0
  25. data/lib/api-tester/definition/method.rb +8 -5
  26. data/lib/api-tester/definition/request.rb +43 -19
  27. data/lib/api-tester/definition/response.rb +35 -25
  28. data/lib/api-tester/method_case_test.rb +68 -54
  29. data/lib/api-tester/modules/benchmark_module.rb +35 -0
  30. data/lib/api-tester/modules/extra_verbs.rb +36 -10
  31. data/lib/api-tester/modules/format.rb +23 -7
  32. data/lib/api-tester/modules/good_case.rb +25 -10
  33. data/lib/api-tester/modules/good_variations.rb +69 -0
  34. data/lib/api-tester/modules/injection_module.rb +44 -23
  35. data/lib/api-tester/modules/missing_resource.rb +63 -0
  36. data/lib/api-tester/modules/required_fields.rb +51 -0
  37. data/lib/api-tester/modules/server_information.rb +14 -12
  38. data/lib/api-tester/modules/typo.rb +39 -14
  39. data/lib/api-tester/modules/unexpected_fields.rb +59 -0
  40. data/lib/api-tester/modules/unused_fields.rb +13 -7
  41. data/lib/api-tester/reporter/api_report.rb +25 -16
  42. data/lib/api-tester/reporter/missing_field_report.rb +11 -15
  43. data/lib/api-tester/reporter/report.rb +12 -13
  44. data/lib/api-tester/reporter/response_time_report.rb +24 -0
  45. data/lib/api-tester/reporter/status_code_report.rb +10 -4
  46. data/lib/api-tester/test_helper.rb +8 -6
  47. data/lib/api-tester/util/response_evaluator.rb +84 -56
  48. data/lib/api-tester/util/supported_verbs.rb +8 -5
  49. data/lib/api-tester/version.rb +3 -1
  50. data/lib/api-tester.rb +6 -3
  51. metadata +92 -27
  52. data/.travis.yml +0 -6
  53. data/lib/api-tester/reporter/missing_response_field_report.rb +0 -21
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/method_case_test'
5
+
6
+ module ApiTester
7
+ # Checks the good case as defined in contract
8
+ module GoodVariations
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ endpoint.methods.each do |method|
14
+ method.request.fields.each do |field|
15
+ field.good_cases.each do |value|
16
+ payload = method.request.default_payload
17
+ payload[field.name] = value
18
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
19
+ payload: payload,
20
+ headers: method.request.default_headers
21
+ response = endpoint.call base_url: contract.base_url,
22
+ method: method,
23
+ payload: payload,
24
+ headers: call.headers
25
+ test = GoodVariationTest.new response: response,
26
+ url: contract.base_url + endpoint.url,
27
+ method: method
28
+ reports.concat test.check
29
+ end
30
+ end
31
+ method.request.query_params.each do |field|
32
+ field.good_cases.each do |value|
33
+ payload = method.request.default_payload
34
+ payload[field.name] = value
35
+ call = BoundaryCase.new description: contract.base_url + endpoint.display_url,
36
+ payload: payload,
37
+ headers: method.request.default_headers
38
+ response = endpoint.call base_url: contract.base_url,
39
+ method: method,
40
+ payload: payload,
41
+ headers: call.headers
42
+ test = GoodVariationTest.new response: response,
43
+ url: contract.base_url + endpoint.url,
44
+ method: method
45
+ reports.concat test.check
46
+ end
47
+ end
48
+ end
49
+ end
50
+ reports
51
+ end
52
+
53
+ def self.order
54
+ 1
55
+ end
56
+ end
57
+
58
+ # Test layout used by module
59
+ class GoodVariationTest < MethodCaseTest
60
+ def initialize(response:, url:, method:)
61
+ super response: response,
62
+ payload: method.request.default_payload,
63
+ expected_response: method.expected_response,
64
+ url: url,
65
+ verb: method.verb,
66
+ module_name: 'GoodVariationsModule'
67
+ end
68
+ end
69
+ end
@@ -1,29 +1,39 @@
1
- require "injection_vulnerability_library"
1
+ # frozen_string_literal: true
2
+
3
+ require 'injection_vulnerability_library'
2
4
 
3
5
  module ApiTester
6
+ # Tests injection cases
4
7
  module InjectionModule
5
- def self.go contract
8
+ def self.go(contract)
6
9
  reports = []
7
10
  contract.endpoints.each do |endpoint|
8
11
  endpoint.methods.each do |method|
9
- reports.concat inject_payload endpoint, method
12
+ reports.concat inject_payload contract.base_url, endpoint, method
10
13
  end
11
14
  end
12
15
  reports
13
16
  end
14
17
 
15
- def self.inject_payload endpoint, method
18
+ def self.inject_payload(base_url, endpoint, method)
16
19
  reports = []
17
20
  sql_injections = InjectionVulnerabilityLibrary.sql_vulnerabilities
18
21
 
19
22
  method.request.fields.each do |field|
20
23
  sql_injections.each do |injection|
21
- injection_value = "#{field.default_value}#{injection}"
22
- payload = method.request.altered_payload(field.name, injection_value)
23
- response = endpoint.call method, payload, method.request.default_headers
24
- if(!check_response(response, endpoint)) then
25
- reports << InjectionReport.new("sql", endpoint.url, payload, response)
26
- end
24
+ injection_value = "#{field.default}#{injection}"
25
+ payload = method.request.altered_payload field_name: field.name,
26
+ value: injection_value
27
+ response = endpoint.call base_url: base_url,
28
+ method: method,
29
+ payload: payload,
30
+ headers: method.request.default_headers
31
+ next if check_response(response, endpoint)
32
+
33
+ reports << InjectionReport.new('sql',
34
+ endpoint.url,
35
+ payload,
36
+ response)
27
37
  end
28
38
  end
29
39
 
@@ -31,25 +41,36 @@ module ApiTester
31
41
  end
32
42
 
33
43
  def self.check_response(response, endpoint)
34
- response.code == 200 || check_error(response, endpoint)
44
+ if response.code == 200 || check_error(response, endpoint)
45
+ print '.'
46
+ return true
47
+ end
48
+ print 'F'
49
+ false
35
50
  end
36
51
 
37
- def self.check_error response, endpoint
38
- evaluator = ApiTester::ResponseEvaluator.new response.body, endpoint.bad_request_response
52
+ def self.check_error(response, endpoint)
53
+ evaluator = ApiTester::ResponseEvaluator.new(
54
+ actual_body: response.body,
55
+ expected_fields: endpoint.bad_request_response
56
+ )
39
57
  missing_fields = evaluator.missing_fields
40
58
  extra_fields = evaluator.extra_fields
41
- response.code == endpoint.bad_request_response.code && missing_fields.size == 0 && extra_fields.size == 0
59
+ response.code == endpoint.bad_request_response.code &&
60
+ missing_fields.size.zero? && extra_fields.size.zero?
61
+ end
62
+
63
+ def self.order
64
+ 5
42
65
  end
43
66
  end
44
67
  end
45
68
 
69
+ # Report for InjectionModule
46
70
  class InjectionReport
47
- attr_accessor :injection_type
48
- attr_accessor :url
49
- attr_accessor :payload
50
- attr_accessor :response
71
+ attr_accessor :injection_type, :url, :payload, :response
51
72
 
52
- def initialize injection_type, url, payload, response
73
+ def initialize(injection_type, url, payload, response)
53
74
  self.injection_type = injection_type
54
75
  self.url = url
55
76
  self.payload = payload
@@ -57,10 +78,10 @@ class InjectionReport
57
78
  end
58
79
 
59
80
  def print
60
- puts "Found potential #{self.injection_type}: "
61
- puts " Requested #{self.url} with payload:"
62
- puts " #{self.payload}"
81
+ puts "Found potential #{injection_type}: "
82
+ puts " Requested #{url} with payload:"
83
+ puts " #{payload}"
63
84
  puts ' Received: '
64
- puts " #{self.response}"
85
+ puts " #{response}"
65
86
  end
66
87
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/status_code_report'
4
+ require 'api-tester/util/supported_verbs'
5
+
6
+ module ApiTester
7
+ # Module checking various not found scenarios
8
+ module MissingResource
9
+ def self.go(contract)
10
+ reports = []
11
+
12
+ contract.endpoints.each do |endpoint|
13
+ endpoint.path_params.each do |path_param|
14
+ bad_resource = endpoint.relative_url.gsub("{#{path_param}}", 'gibberish')
15
+
16
+ bad_endpoint = ApiTester::Endpoint.new name: 'Bad Resource',
17
+ relative_url: bad_resource
18
+ method = ApiTester::Method.new verb: ApiTester::SupportedVerbs::GET,
19
+ response: ApiTester::Response.new(
20
+ status_code: 200
21
+ ),
22
+ request: ApiTester::Request.new
23
+ response = bad_endpoint.call base_url: contract.base_url + bad_resource,
24
+ method: method,
25
+ payload: {},
26
+ headers: contract.required_headers
27
+ test = MissingResourceTest.new response,
28
+ {},
29
+ endpoint.not_found_response,
30
+ bad_resource,
31
+ ApiTester::SupportedVerbs::GET
32
+ test.check
33
+ end
34
+ end
35
+
36
+ reports
37
+ end
38
+
39
+ def self.allowed_verbs(endpoint)
40
+ allowances = []
41
+ endpoint.methods.each do |method|
42
+ allowances << method.verb
43
+ end
44
+ allowances.uniq
45
+ end
46
+
47
+ def self.order
48
+ 4
49
+ end
50
+ end
51
+
52
+ # Test layout for Missing Resource
53
+ class MissingResourceTest < MethodCaseTest
54
+ def initialize(response, payload, expected_response, url, verb)
55
+ super response: response,
56
+ payload: payload,
57
+ expected_response: expected_response,
58
+ url: url,
59
+ verb: verb,
60
+ module_name: 'Missing Resource'
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Ensures the fields marked as required in contract are guarded
5
+ module RequiredFields
6
+ def self.go(contract)
7
+ reports = []
8
+ contract.endpoints.each do |endpoint|
9
+ endpoint.methods.each do |method|
10
+ request_def = method.request
11
+ required_fields = request_def.fields.keep_if(&:required)
12
+ combinations = (1..required_fields.size).flat_map { |size| required_fields.combination(size).to_a }
13
+ combinations.each do |remove_fields|
14
+ fields = remove_fields.map do |field|
15
+ { name: field.name, value: nil }
16
+ end
17
+ payload = request_def.altered_payload_with fields
18
+ response = endpoint.call base_url: contract.base_url,
19
+ method: method,
20
+ payload: payload,
21
+ headers: request_def.default_headers
22
+ test = RequiredFieldsTest.new response: response,
23
+ payload: payload,
24
+ expected_response: endpoint.bad_request_response,
25
+ url: endpoint.url,
26
+ verb: method.verb
27
+ reports.concat test.check
28
+ end
29
+ end
30
+ end
31
+
32
+ reports
33
+ end
34
+
35
+ def self.order
36
+ 5
37
+ end
38
+ end
39
+
40
+ # Test layout used for RequiredFieldsModule
41
+ class RequiredFieldsTest < MethodCaseTest
42
+ def initialize(response:, payload:, expected_response:, url:, verb:)
43
+ super response: response,
44
+ payload: payload,
45
+ expected_response: expected_response,
46
+ url: url,
47
+ verb: verb,
48
+ module_name: 'RequiredFieldsModule'
49
+ end
50
+ end
51
+ end
@@ -1,15 +1,17 @@
1
- require 'pry'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ApiTester
4
+ # Module for ensuring the server isn't broadcasting information about itself
4
5
  module ServerInformation
5
- def self.go contract
6
+ def self.go(contract)
6
7
  reports = []
7
8
  endpoint = contract.endpoints[0]
8
- response = endpoint.default_call
9
+ response = endpoint.default_call contract.base_url
9
10
 
10
- [:server, :x_powered_by, :x_aspnetmvc_version, :x_aspnet_version].each do |server_key|
11
- if response.headers[server_key] then
12
- reports << ServerBroadcastReport.new(response.headers[server_key], server_key)
11
+ %i[server x_powered_by x_aspnetmvc_version x_aspnet_version].each do |key|
12
+ if response.headers[key]
13
+ reports << ServerBroadcastReport.new(response.headers[key],
14
+ key)
13
15
  end
14
16
  end
15
17
 
@@ -22,18 +24,18 @@ module ApiTester
22
24
  end
23
25
  end
24
26
 
27
+ # Report used by module
25
28
  class ServerBroadcastReport
26
- attr_accessor :server_info
27
- attr_accessor :server_key
29
+ attr_accessor :server_info, :server_key
28
30
 
29
- def initialize server_info, server_key
31
+ def initialize(server_info, server_key)
30
32
  self.server_info = server_info
31
33
  self.server_key = server_key
32
34
  end
33
35
 
34
36
  def print
35
- puts "Found server information being broadcast in headers:"
36
- puts " #{self.server_info}"
37
- puts " as #{self.server_key}"
37
+ puts 'Found server information being broadcast in headers:'
38
+ puts " #{server_info}"
39
+ puts " as #{server_key}"
38
40
  end
39
41
  end
@@ -1,35 +1,54 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'api-tester/reporter/status_code_report'
2
4
  require 'api-tester/util/supported_verbs'
3
5
 
4
6
  module ApiTester
5
- class Typo
6
- def self.go contract
7
+ # Module checking various not found scenarios
8
+ module Typo
9
+ def self.go(contract)
7
10
  reports = []
8
11
 
9
- contract.endpoints.each do |endpoint|
10
- allowances(endpoint).each do |verbs|
11
- reports.concat check_typo_url(endpoint)
12
+ # Filtering out endpoints with ids since not a better way to check this
13
+ # Need to redesign system to handle this better
14
+ contract.endpoints.reject { |e| e.relative_url.include?('{') }.each do |endpoint|
15
+ allowances(endpoint).each do
16
+ reports.concat check_typo_url(contract.base_url, endpoint)
12
17
  end
13
18
  end
14
19
 
15
20
  reports
16
21
  end
17
22
 
18
- def self.check_typo_url endpoint
23
+ def self.check_typo_url(base_url, endpoint)
19
24
  bad_url = "#{endpoint.url}gibberishadsfasdf"
20
- bad_endpoint = ApiTester::Endpoint.new "Bad URL", bad_url
21
- typo_case = BoundaryCase.new("Typo URL check", {}, {})
22
- method = ApiTester::Method.new ApiTester::SupportedVerbs::GET, ApiTester::Response.new(200), ApiTester::Request.new
23
- response = bad_endpoint.call method, typo_case.payload, typo_case.headers
25
+ bad_endpoint = ApiTester::Endpoint.new name: 'Bad URL',
26
+ relative_url: bad_url
27
+ typo_case = BoundaryCase.new description: 'Typo URL check',
28
+ payload: {},
29
+ headers: {}
30
+ method = ApiTester::Method.new verb: ApiTester::SupportedVerbs::GET,
31
+ response: ApiTester::Response.new(
32
+ status_code: 200
33
+ ),
34
+ request: ApiTester::Request.new
35
+ response = bad_endpoint.call base_url: base_url,
36
+ method: method,
37
+ payload: typo_case.payload,
38
+ headers: typo_case.headers
24
39
 
25
- test = TypoClass.new response, typo_case.payload, endpoint.not_found_response, bad_url, ApiTester::SupportedVerbs::GET
40
+ test = TypoClass.new response,
41
+ typo_case.payload,
42
+ endpoint.not_found_response,
43
+ bad_url,
44
+ ApiTester::SupportedVerbs::GET
26
45
  test.check
27
46
  end
28
47
 
29
48
  def self.allowances(endpoint)
30
49
  allowances = []
31
50
  endpoint.methods.each do |method|
32
- allowances << method.verb
51
+ allowances << method.verb
33
52
  end
34
53
  allowances.uniq
35
54
  end
@@ -39,9 +58,15 @@ module ApiTester
39
58
  end
40
59
  end
41
60
 
61
+ # Test layout for TypoModule
42
62
  class TypoClass < MethodCaseTest
43
- def initialize response, payload, expected_response, url, verb
44
- super response, payload, expected_response, url, verb, "TypoModule"
63
+ def initialize(response, payload, expected_response, url, verb)
64
+ super response: response,
65
+ payload: payload,
66
+ expected_response: expected_response,
67
+ url: url,
68
+ verb: verb,
69
+ module_name: 'TypoModule'
45
70
  end
46
71
  end
47
72
  end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Module checking nothing shows up in response which is not defined in contract
5
+ module UnexpectedFields
6
+ def self.go(contract)
7
+ reports = []
8
+
9
+ contract.endpoints.each do |endpoint|
10
+ endpoint.methods.each do |method|
11
+ default_case = BoundaryCase.new description: endpoint.url,
12
+ payload: method.request.default_payload,
13
+ headers: method.request.default_headers
14
+ response = endpoint.call base_url: contract.base_url,
15
+ method: method,
16
+ payload: default_case.payload,
17
+ headers: default_case.headers
18
+ test = UnexpectedFieldsTest.new response, endpoint.url, method
19
+ reports.concat test.check
20
+ end
21
+ end
22
+ reports
23
+ end
24
+
25
+ def self.order
26
+ 90
27
+ end
28
+ end
29
+
30
+ # Test layout for UnexpectedFields module
31
+ class UnexpectedFieldsTest < MethodCaseTest
32
+ def initialize(response, url, method)
33
+ super response: response,
34
+ payload: method.request.default_payload,
35
+ expected_response: method.expected_response,
36
+ url: url,
37
+ verb: method.verb,
38
+ module_name: 'UnexpectedFieldsModule'
39
+ end
40
+
41
+ def check
42
+ evaluator = ApiTester::ResponseEvaluator.new actual_body: json_parse(response.body),
43
+ expected_fields: expected_response
44
+ response_fields = evaluator.response_field_array
45
+ expected_fields = evaluator.expected_fields
46
+ increment_fields evaluator.seen_fields
47
+ extra = response_fields - expected_fields
48
+ extra.each do |extra_field|
49
+ report = Report.new description: "UnexpectedFieldsModule - Found unexpected field #{extra_field}",
50
+ url: url,
51
+ request: payload,
52
+ expected_response: expected_response,
53
+ actual_response: response
54
+ reports << report
55
+ end
56
+ reports
57
+ end
58
+ end
59
+ end
@@ -1,16 +1,22 @@
1
- require 'api-tester/reporter/missing_response_field_report'
1
+ # frozen_string_literal: true
2
+
3
+ require 'api-tester/reporter/missing_field_report'
2
4
 
3
5
  module ApiTester
4
- class UnusedFields
5
- def self.go contract
6
+ # Ensures all fields defined in contract are returned during test suite
7
+ module UnusedFields
8
+ def self.go(contract)
6
9
  reports = []
7
10
 
8
11
  contract.endpoints.each do |endpoint|
9
12
  endpoint.methods.each do |method|
10
- method.expected_response.body.each do |field|
11
- if field.is_seen == 0
12
- reports << MissingResponseFieldReport.new(endpoint.url, method.verb, field.name, "UnusedFieldsModule")
13
- end
13
+ method.expected_response.body.filter(&:has_key).each do |field|
14
+ next unless field.is_seen.zero?
15
+
16
+ reports << MissingFieldReport.new(url: endpoint.url,
17
+ verb: method.verb,
18
+ expected_field: field.name,
19
+ description: 'UnusedFieldsModule')
14
20
  end
15
21
  end
16
22
  end
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'api-tester/reporter/report'
2
4
 
3
5
  module ApiTester
6
+ # class for dealing with reports generated by the modules during test suite
4
7
  class ApiReport
5
8
  attr_accessor :reports
6
9
 
@@ -8,31 +11,37 @@ module ApiTester
8
11
  self.reports = []
9
12
  end
10
13
 
11
- def add_new url, request, expected_response, actual_response, description="A case"
12
- report = Report.new description, url, request, expected_response, actual_response
13
- self.reports << report
14
+ def add_new(url:, request:, expected_response:, actual_response:, description: 'case')
15
+ report = Report.new description,
16
+ url,
17
+ request,
18
+ expected_response,
19
+ actual_response
20
+ reports << report
14
21
  end
15
22
 
16
- def add_new_report report
17
- self.reports << report
23
+ def add_new_report(report)
24
+ reports << report
18
25
  end
19
26
 
20
- def add_reports reports
21
- self.reports.concat reports
27
+ def add_reports(reports)
28
+ reports.each do |report|
29
+ add_new_report(report)
30
+ end
22
31
  end
23
32
 
24
33
  def print
25
- if self.reports.size > 0
26
- puts "Issues discovered: #{self.reports.size}"
27
- self.reports.each do |report|
34
+ puts ''
35
+ if reports.size.zero?
36
+ puts 'No issues found'
37
+ else
38
+ puts "Issues discovered: #{reports.size}"
39
+ reports.each do |report|
28
40
  report.print
29
- puts "\n"
30
- puts "\n"
41
+ puts ''
42
+ puts ''
31
43
  end
32
- puts ""
33
- puts "Issues discovered: #{self.reports.size}"
34
- else
35
- puts "No issues found"
44
+ puts "Total issues: #{reports.size}"
36
45
  end
37
46
  end
38
47
  end
@@ -1,25 +1,21 @@
1
- module ApiReport
1
+ # frozen_string_literal: true
2
+
3
+ module ApiTester
4
+ # Report used for when response is missing a field
2
5
  class MissingFieldReport
3
- attr_accessor :description
4
- attr_accessor :url
5
- attr_accessor :request
6
- attr_accessor :expected_field
7
- attr_accessor :actual_response
6
+ attr_accessor :url, :verb, :expected_field, :description
8
7
 
9
- def initialize(description, url, request, expected_field)
10
- self.description = description
8
+ def initialize(url:, verb:, expected_field:, description:)
11
9
  self.url = url
12
- self.request = request
10
+ self.verb = verb
13
11
  self.expected_field = expected_field
14
- self.actual_response = ''
12
+ self.description = description
15
13
  end
16
14
 
17
15
  def print
18
- puts "#{self.description}: "
19
- puts " Requested #{self.url} with payload:"
20
- puts " #{self.request}"
21
- puts ' Missing field: '
22
- puts " #{self.expected_field}"
16
+ puts "#{description}:"
17
+ puts " #{verb} #{url} is missing response field:"
18
+ puts " #{expected_field}"
23
19
  end
24
20
  end
25
21
  end
@@ -1,12 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ApiTester
4
+ # Standard report format for differing responses
2
5
  class Report
3
- attr_accessor :description
4
- attr_accessor :url
5
- attr_accessor :request
6
- attr_accessor :expected_response
7
- attr_accessor :actual_response
6
+ attr_accessor :description, :url, :request, :expected_response, :actual_response
8
7
 
9
- def initialize description, url, request, expected_response, actual_response
8
+ def initialize(description:, url:, request:, expected_response:, actual_response:)
10
9
  self.description = description
11
10
  self.url = url
12
11
  self.request = request
@@ -15,13 +14,13 @@ module ApiTester
15
14
  end
16
15
 
17
16
  def print
18
- puts "#{self.description}: "
19
- puts " Requested #{self.url} with payload:"
20
- puts " #{self.request.to_json}"
21
- puts " Expecting: "
22
- puts " " + self.expected_response.to_s
23
- puts " Receiving: "
24
- puts " #{self.actual_response}"
17
+ puts "#{description}: "
18
+ puts " Requested #{url} with payload:"
19
+ puts " #{request.to_json}"
20
+ puts ' Expecting: '
21
+ puts " #{expected_response}"
22
+ puts ' Receiving: '
23
+ puts " #{actual_response}"
25
24
  end
26
25
  end
27
26
  end