api-tester 1.0.0 → 1.1.1
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.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/push.yml +39 -0
- data/.github/workflows/test.yml +31 -0
- data/.rubocop.yml +61 -0
- data/Gemfile +2 -0
- data/Guardfile +70 -0
- data/README.md +65 -61
- data/Rakefile +8 -3
- data/api-tester.gemspec +29 -24
- data/changelog.txt +10 -0
- data/lib/api-tester.rb +6 -3
- data/lib/api-tester/config.rb +16 -13
- data/lib/api-tester/definition/boundary_case.rb +4 -1
- data/lib/api-tester/definition/contract.rb +8 -3
- data/lib/api-tester/definition/endpoint.rb +32 -23
- data/lib/api-tester/definition/fields/array_field.rb +20 -19
- data/lib/api-tester/definition/fields/boolean_field.rb +12 -9
- data/lib/api-tester/definition/fields/email_field.rb +14 -11
- data/lib/api-tester/definition/fields/enum_field.rb +14 -11
- data/lib/api-tester/definition/fields/field.rb +46 -45
- data/lib/api-tester/definition/fields/number_field.rb +11 -8
- data/lib/api-tester/definition/fields/object_field.rb +34 -31
- data/lib/api-tester/definition/fields/plain_array_field.rb +25 -0
- data/lib/api-tester/definition/method.rb +7 -2
- data/lib/api-tester/definition/request.rb +43 -16
- data/lib/api-tester/definition/response.rb +29 -26
- data/lib/api-tester/method_case_test.rb +67 -53
- data/lib/api-tester/modules/extra_verbs.rb +29 -9
- data/lib/api-tester/modules/format.rb +23 -7
- data/lib/api-tester/modules/good_case.rb +25 -10
- data/lib/api-tester/modules/injection_module.rb +32 -17
- data/lib/api-tester/modules/required_fields.rb +51 -0
- data/lib/api-tester/modules/server_information.rb +13 -10
- data/lib/api-tester/modules/typo.rb +36 -13
- data/lib/api-tester/modules/unexpected_fields.rb +61 -0
- data/lib/api-tester/modules/unused_fields.rb +12 -6
- data/lib/api-tester/reporter/api_report.rb +24 -16
- data/lib/api-tester/reporter/missing_field_report.rb +12 -13
- data/lib/api-tester/reporter/report.rb +11 -8
- data/lib/api-tester/reporter/status_code_report.rb +9 -2
- data/lib/api-tester/test_helper.rb +6 -6
- data/lib/api-tester/util/response_evaluator.rb +70 -57
- data/lib/api-tester/util/supported_verbs.rb +8 -5
- data/lib/api-tester/version.rb +3 -1
- metadata +99 -25
- data/.travis.yml +0 -6
- data/lib/api-tester/reporter/missing_response_field_report.rb +0 -21
@@ -1,14 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'api-tester/definition/fields/field'
|
2
4
|
|
3
5
|
module ApiTester
|
6
|
+
# Class for defining enumerators
|
4
7
|
class EnumField < Field
|
5
8
|
attr_accessor :acceptable_values
|
6
9
|
|
7
|
-
def initialize
|
10
|
+
def initialize(name:, acceptable_values:, default_value: nil, required: false)
|
8
11
|
if default_value
|
9
|
-
super name: name, default_value: default_value
|
12
|
+
super name: name, default_value: default_value, required: required
|
10
13
|
else
|
11
|
-
super name: name, default_value: acceptable_values[0]
|
14
|
+
super name: name, default_value: acceptable_values[0], required: required
|
12
15
|
end
|
13
16
|
|
14
17
|
self.acceptable_values = acceptable_values
|
@@ -16,14 +19,14 @@ module ApiTester
|
|
16
19
|
|
17
20
|
def negative_boundary_values
|
18
21
|
super +
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
22
|
+
[
|
23
|
+
123,
|
24
|
+
0,
|
25
|
+
1,
|
26
|
+
true,
|
27
|
+
false,
|
28
|
+
{}
|
29
|
+
]
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
@@ -1,49 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiTester
|
4
|
+
# Base class for field definitions
|
2
5
|
class Field
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
self.class
|
47
|
-
end
|
6
|
+
attr_accessor :name
|
7
|
+
attr_accessor :default_value
|
8
|
+
attr_accessor :required
|
9
|
+
attr_accessor :is_seen
|
10
|
+
|
11
|
+
def initialize(name:, required: false, default_value: 'string')
|
12
|
+
self.name = name
|
13
|
+
self.default_value = default_value
|
14
|
+
self.required = required
|
15
|
+
self.is_seen = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_required
|
19
|
+
self.required = true
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def is_not_required
|
24
|
+
self.required = false
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def subfields?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def fields
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
|
36
|
+
def negative_boundary_values
|
37
|
+
cases = []
|
38
|
+
cases << nil if required
|
39
|
+
cases
|
40
|
+
end
|
41
|
+
|
42
|
+
def seen
|
43
|
+
self.is_seen += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def display_class
|
47
|
+
self.class
|
48
|
+
end
|
48
49
|
end
|
49
50
|
end
|
@@ -1,19 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'api-tester/definition/fields/field'
|
2
4
|
|
3
5
|
module ApiTester
|
6
|
+
# Class for defining numeric fields in contracts
|
4
7
|
class NumberField < Field
|
5
|
-
def initialize
|
6
|
-
super name: name, default_value: default_value
|
8
|
+
def initialize(name:, default_value: 5, required: false)
|
9
|
+
super name: name, default_value: default_value, required: required
|
7
10
|
end
|
8
11
|
|
9
12
|
def negative_boundary_values
|
10
13
|
super +
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
[
|
15
|
+
'string',
|
16
|
+
true,
|
17
|
+
false,
|
18
|
+
{}
|
19
|
+
]
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
@@ -1,44 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'api-tester/definition/fields/field'
|
2
4
|
|
3
5
|
module ApiTester
|
6
|
+
# Class for defining objects in a contract
|
4
7
|
class ObjectField < Field
|
5
|
-
|
6
|
-
|
7
|
-
def initialize name:
|
8
|
-
super name: name
|
9
|
-
self.fields = []
|
10
|
-
end
|
8
|
+
attr_accessor :fields
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
def initialize(name:, required: false)
|
11
|
+
super name: name, required: required
|
12
|
+
self.fields = []
|
13
|
+
end
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
def with_field(new_field)
|
16
|
+
fields << new_field
|
17
|
+
self
|
18
|
+
end
|
20
19
|
|
21
|
-
|
22
|
-
|
20
|
+
def subfields?
|
21
|
+
true
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
end
|
24
|
+
def default_value
|
25
|
+
obj = {}
|
27
26
|
|
28
|
-
|
27
|
+
fields.each do |field|
|
28
|
+
obj[field.name] = field.default_value
|
29
29
|
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
31
|
+
obj
|
32
|
+
end
|
33
|
+
|
34
|
+
def negative_boundary_values
|
35
|
+
super +
|
36
|
+
[
|
37
|
+
'string',
|
38
|
+
[],
|
39
|
+
123,
|
40
|
+
1,
|
41
|
+
0,
|
42
|
+
true,
|
43
|
+
false
|
44
|
+
]
|
45
|
+
end
|
43
46
|
end
|
44
47
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'api-tester/definition/fields/field'
|
4
|
+
|
5
|
+
module ApiTester
|
6
|
+
# Class for defining plain arrays
|
7
|
+
class PlainArrayField < Field
|
8
|
+
def initialize(name:, default_value: [], required: false)
|
9
|
+
super name: name, default_value: default_value, required: required
|
10
|
+
end
|
11
|
+
|
12
|
+
def negative_boundary_values
|
13
|
+
super +
|
14
|
+
[
|
15
|
+
'string',
|
16
|
+
123,
|
17
|
+
0,
|
18
|
+
1,
|
19
|
+
{},
|
20
|
+
true,
|
21
|
+
false
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,17 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiTester
|
4
|
+
# Class for defining methods as part of an endpoint
|
2
5
|
class Method
|
3
6
|
attr_accessor :request
|
4
7
|
attr_accessor :expected_response
|
5
8
|
attr_accessor :verb
|
6
9
|
|
7
|
-
def initialize
|
10
|
+
def initialize(verb:, response:, request:)
|
8
11
|
self.verb = verb
|
9
12
|
self.request = request
|
10
13
|
self.expected_response = response
|
11
14
|
end
|
12
15
|
|
13
16
|
def default_request
|
14
|
-
{:
|
17
|
+
{ method: verb,
|
18
|
+
payload: request.default_payload,
|
19
|
+
headers: request.default_headers }
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|
@@ -1,30 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'api-tester/definition/boundary_case'
|
2
4
|
|
3
5
|
module ApiTester
|
6
|
+
# Class for defining requests in a contract
|
4
7
|
class Request
|
5
8
|
attr_accessor :definition
|
6
9
|
attr_accessor :header_fields
|
7
10
|
attr_accessor :fields
|
11
|
+
attr_accessor :query_params
|
8
12
|
|
9
13
|
def initialize
|
10
14
|
self.fields = []
|
11
15
|
self.header_fields = []
|
16
|
+
self.query_params = []
|
12
17
|
end
|
13
18
|
|
14
19
|
def add_field(new_field)
|
15
|
-
|
20
|
+
fields << new_field
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_query_param(new_query_param)
|
25
|
+
query_params << new_query_param
|
16
26
|
self
|
17
27
|
end
|
18
28
|
|
19
|
-
def
|
20
|
-
|
29
|
+
def default_query
|
30
|
+
query_params.map { |param| "#{param.name}=#{param.default_value}" }.join('&')
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_header_field(new_header)
|
34
|
+
header_fields << new_header
|
21
35
|
self
|
22
36
|
end
|
23
37
|
|
24
38
|
def payload
|
25
|
-
response =
|
26
|
-
|
27
|
-
|
39
|
+
response = {}
|
40
|
+
fields.each do |field|
|
41
|
+
if field.required == true
|
42
|
+
response[field.name] = field.default_value
|
43
|
+
end
|
28
44
|
end
|
29
45
|
response
|
30
46
|
end
|
@@ -34,36 +50,47 @@ module ApiTester
|
|
34
50
|
end
|
35
51
|
|
36
52
|
def default_headers
|
37
|
-
if
|
38
|
-
|
53
|
+
if header_fields != []
|
54
|
+
headers
|
39
55
|
else
|
40
|
-
{content_type: :json, accept: :json}
|
56
|
+
{ content_type: :json, accept: :json }
|
41
57
|
end
|
42
58
|
end
|
43
59
|
|
44
60
|
def headers
|
45
61
|
header_response = {}
|
46
|
-
|
47
|
-
header_response[
|
62
|
+
header_fields.each do |header_field|
|
63
|
+
header_response[header_field.name] = header_field.default_value
|
48
64
|
end
|
49
65
|
header_response
|
50
66
|
end
|
51
67
|
|
52
68
|
def cases
|
53
|
-
boundary_cases =
|
54
|
-
|
69
|
+
boundary_cases = []
|
70
|
+
fields.each do |field|
|
55
71
|
field.negative_boundary_values.each do |value|
|
56
|
-
bcase = BoundaryCase.new
|
57
|
-
|
72
|
+
bcase = BoundaryCase.new description: "Setting #{field.name} to #{value}",
|
73
|
+
payload: altered_payload(field_name: field.name,
|
74
|
+
value: value),
|
75
|
+
headers: default_headers
|
76
|
+
boundary_cases.push bcase
|
58
77
|
end
|
59
78
|
end
|
60
79
|
boundary_cases
|
61
80
|
end
|
62
81
|
|
63
|
-
def altered_payload
|
82
|
+
def altered_payload(field_name:, value:)
|
64
83
|
body = payload
|
65
84
|
body[field_name] = value
|
66
85
|
body
|
67
86
|
end
|
87
|
+
|
88
|
+
def altered_payload_with(fields)
|
89
|
+
body = payload
|
90
|
+
fields.each do |field|
|
91
|
+
body[field[:name]] = field[:value]
|
92
|
+
end
|
93
|
+
body
|
94
|
+
end
|
68
95
|
end
|
69
96
|
end
|
@@ -1,36 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ApiTester
|
4
|
+
# Class for defining expected responses
|
2
5
|
class Response
|
3
|
-
|
4
|
-
|
6
|
+
attr_accessor :code
|
7
|
+
attr_accessor :body
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
def initialize(status_code: 200)
|
10
|
+
self.code = status_code
|
11
|
+
self.body = []
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
def add_field(new_field)
|
15
|
+
body << new_field
|
16
|
+
self
|
17
|
+
end
|
15
18
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
des.to_json
|
19
|
+
def to_s
|
20
|
+
des = {}
|
21
|
+
body.map do |f|
|
22
|
+
des[f.name] = field_display f
|
22
23
|
end
|
24
|
+
des.to_json
|
25
|
+
end
|
23
26
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
end
|
33
|
-
des
|
27
|
+
def field_display(field)
|
28
|
+
des = field.display_class
|
29
|
+
if field.subfields?
|
30
|
+
des = {}
|
31
|
+
field.fields.map do |f|
|
32
|
+
des[f.name] = field_display f
|
33
|
+
end
|
34
|
+
des.to_json
|
34
35
|
end
|
36
|
+
des
|
37
|
+
end
|
35
38
|
end
|
36
39
|
end
|