api-tester 0.1.0 → 0.2.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.
- checksums.yaml +4 -4
- data/README.md +12 -12
- data/api-tester.gemspec +2 -2
- data/changelog.txt +5 -0
- data/lib/api-tester.rb +14 -0
- data/lib/api-tester/config.rb +40 -0
- data/lib/api-tester/definition/api_contract.rb +15 -0
- data/lib/api-tester/definition/api_method.rb +13 -0
- data/lib/api-tester/definition/boundary_case.rb +13 -0
- data/lib/api-tester/definition/endpoint.rb +61 -0
- data/lib/api-tester/definition/fields/array_field.rb +46 -0
- data/lib/api-tester/definition/fields/boolean_field.rb +20 -0
- data/lib/api-tester/definition/fields/email_field.rb +22 -0
- data/lib/api-tester/definition/fields/enum_field.rb +29 -0
- data/lib/api-tester/definition/fields/field.rb +49 -0
- data/lib/api-tester/definition/fields/number_field.rb +19 -0
- data/lib/api-tester/definition/fields/object_field.rb +44 -0
- data/lib/api-tester/definition/request.rb +51 -0
- data/lib/api-tester/definition/response.rb +36 -0
- data/lib/api-tester/method_case_test.rb +69 -0
- data/lib/api-tester/modules/extra_verbs.rb +27 -0
- data/lib/api-tester/modules/format.rb +28 -0
- data/lib/api-tester/modules/good_case.rb +31 -0
- data/lib/api-tester/modules/module.rb +20 -0
- data/lib/api-tester/modules/typo.rb +43 -0
- data/lib/api-tester/modules/unused_fields.rb +24 -0
- data/lib/api-tester/reporter/api_report.rb +35 -0
- data/lib/api-tester/reporter/missing_field_report.rb +25 -0
- data/lib/api-tester/reporter/missing_response_field_report.rb +21 -0
- data/lib/api-tester/reporter/report.rb +27 -0
- data/lib/api-tester/reporter/status_code_report.rb +14 -0
- data/lib/api-tester/test_helper.rb +12 -0
- data/lib/api-tester/util/response_evaluator.rb +75 -0
- data/lib/api-tester/util/supported_verbs.rb +36 -0
- data/lib/api-tester/version.rb +3 -0
- metadata +33 -33
- data/lib/tester.rb +0 -7
- data/lib/tester/api_tester.rb +0 -50
- data/lib/tester/definition/api_contract.rb +0 -13
- data/lib/tester/definition/api_method.rb +0 -11
- data/lib/tester/definition/boundary_case.rb +0 -11
- data/lib/tester/definition/endpoint.rb +0 -57
- data/lib/tester/definition/fields/array_field.rb +0 -44
- data/lib/tester/definition/fields/boolean_field.rb +0 -18
- data/lib/tester/definition/fields/email_field.rb +0 -20
- data/lib/tester/definition/fields/enum_field.rb +0 -27
- data/lib/tester/definition/fields/field.rb +0 -47
- data/lib/tester/definition/fields/number_field.rb +0 -17
- data/lib/tester/definition/fields/object_field.rb +0 -42
- data/lib/tester/definition/request.rb +0 -49
- data/lib/tester/definition/response.rb +0 -34
- data/lib/tester/method_case_test.rb +0 -67
- data/lib/tester/modules/extra_verbs.rb +0 -25
- data/lib/tester/modules/format.rb +0 -26
- data/lib/tester/modules/good_case.rb +0 -29
- data/lib/tester/modules/module.rb +0 -18
- data/lib/tester/modules/typo.rb +0 -41
- data/lib/tester/modules/unused_fields.rb +0 -22
- data/lib/tester/reporter/api_report.rb +0 -33
- data/lib/tester/reporter/missing_field_report.rb +0 -23
- data/lib/tester/reporter/missing_response_field_report.rb +0 -19
- data/lib/tester/reporter/report.rb +0 -25
- data/lib/tester/reporter/status_code_report.rb +0 -12
- data/lib/tester/test_helper.rb +0 -10
- data/lib/tester/util/response_evaluator.rb +0 -73
- data/lib/tester/util/supported_verbs.rb +0 -34
- data/lib/tester/version.rb +0 -3
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'api-tester/definition/fields/field'
|
2
|
+
|
3
|
+
module ApiTester
|
4
|
+
class ObjectField < Field
|
5
|
+
attr_accessor :fields
|
6
|
+
|
7
|
+
def initialize name
|
8
|
+
super(name)
|
9
|
+
self.fields = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def with_field(newField)
|
13
|
+
self.fields << newField
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def has_subfields?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def default_value
|
22
|
+
obj = Hash.new
|
23
|
+
|
24
|
+
self.fields.each do |field|
|
25
|
+
obj[field.name] = field.default_value
|
26
|
+
end
|
27
|
+
|
28
|
+
obj
|
29
|
+
end
|
30
|
+
|
31
|
+
def negative_boundary_values
|
32
|
+
super +
|
33
|
+
[
|
34
|
+
"string",
|
35
|
+
[],
|
36
|
+
123,
|
37
|
+
1,
|
38
|
+
0,
|
39
|
+
true,
|
40
|
+
false
|
41
|
+
]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'api-tester/definition/boundary_case'
|
2
|
+
|
3
|
+
module ApiTester
|
4
|
+
class Request
|
5
|
+
attr_accessor :definition
|
6
|
+
attr_accessor :headers
|
7
|
+
attr_accessor :fields
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
self.fields = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_field(new_field)
|
14
|
+
self.fields << new_field
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def payload
|
19
|
+
response = Hash.new
|
20
|
+
self.fields.each do |field|
|
21
|
+
response[field.name] = field.default_value
|
22
|
+
end
|
23
|
+
response
|
24
|
+
end
|
25
|
+
|
26
|
+
def default_payload
|
27
|
+
payload
|
28
|
+
end
|
29
|
+
|
30
|
+
def default_headers
|
31
|
+
{content_type: :json, accept: :json}
|
32
|
+
end
|
33
|
+
|
34
|
+
def cases
|
35
|
+
boundary_cases = Array.new
|
36
|
+
self.fields.each do |field|
|
37
|
+
field.negative_boundary_values.each do |value|
|
38
|
+
bcase = BoundaryCase.new("Setting #{field.name} to #{value}", altered_payload(field.name, value), default_headers)
|
39
|
+
boundary_cases.push(bcase)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
boundary_cases
|
43
|
+
end
|
44
|
+
|
45
|
+
def altered_payload field_name, value
|
46
|
+
body = payload
|
47
|
+
body[field_name] = value
|
48
|
+
body
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ApiTester
|
2
|
+
class Response
|
3
|
+
attr_accessor :code
|
4
|
+
attr_accessor :body
|
5
|
+
|
6
|
+
def initialize(status_code=200)
|
7
|
+
self.code = status_code
|
8
|
+
self.body = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_field(new_field)
|
12
|
+
self.body << new_field
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_s
|
17
|
+
des = {}
|
18
|
+
self.body.map do |f|
|
19
|
+
des[f.name] = field_display f
|
20
|
+
end
|
21
|
+
des.to_json
|
22
|
+
end
|
23
|
+
|
24
|
+
def field_display field
|
25
|
+
des = field.display_class
|
26
|
+
if field.has_subfields?
|
27
|
+
des = {}
|
28
|
+
field.fields.map do |f|
|
29
|
+
des[f.name] = field_display f
|
30
|
+
end
|
31
|
+
des.to_json
|
32
|
+
end
|
33
|
+
des
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'api-tester/util/response_evaluator.rb'
|
2
|
+
|
3
|
+
module ApiTester
|
4
|
+
class MethodCaseTest
|
5
|
+
attr_accessor :expected_response
|
6
|
+
attr_accessor :payload
|
7
|
+
attr_accessor :response
|
8
|
+
attr_accessor :reports
|
9
|
+
attr_accessor :url
|
10
|
+
attr_accessor :module_name
|
11
|
+
|
12
|
+
def initialize response, payload, expected_response, url, verb, module_name
|
13
|
+
self.payload = payload
|
14
|
+
self.response = response
|
15
|
+
self.expected_response = expected_response
|
16
|
+
self.reports = []
|
17
|
+
self.url = "#{verb} #{url}"
|
18
|
+
self.module_name = module_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def response_code_report
|
22
|
+
report = StatusCodeReport.new "#{module_name} - Incorrect response code", self.url, self.payload, self.expected_response.code, self.response.code
|
23
|
+
self.reports << report
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def missing_field_report field
|
28
|
+
report = Report.new "#{module_name} - Missing field #{field}", self.url, self.payload, self.expected_response, self.response
|
29
|
+
self.reports << report
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def extra_field_report field
|
34
|
+
report = Report.new "#{module_name} - Found extra field #{field}", self.url, self.payload, self.expected_response, self.response
|
35
|
+
self.reports << report
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
def check
|
40
|
+
if check_response_code
|
41
|
+
evaluator = ApiTester::ResponseEvaluator.new json_parse(self.response.body), self.expected_response
|
42
|
+
evaluator.missing_fields.map{|field| missing_field_report(field)}
|
43
|
+
evaluator.extra_fields.map{|field| extra_field_report(field)}
|
44
|
+
increment_fields evaluator.seen_fields
|
45
|
+
end
|
46
|
+
return self.reports
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_response_code
|
50
|
+
if response.code != expected_response.code
|
51
|
+
response_code_report
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
return true
|
55
|
+
end
|
56
|
+
|
57
|
+
def increment_fields seen_fields
|
58
|
+
seen_fields.each do |field|
|
59
|
+
field.seen
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def json_parse body
|
64
|
+
JSON.parse!(body)
|
65
|
+
rescue JSON::ParserError
|
66
|
+
body
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'api-tester/modules/module'
|
2
|
+
require 'api-tester/util/supported_verbs'
|
3
|
+
|
4
|
+
module ApiTester
|
5
|
+
class ExtraVerbs < Module
|
6
|
+
def go(endpoint, report)
|
7
|
+
super
|
8
|
+
|
9
|
+
extras = ApiTester::SupportedVerbs.all - endpoint.verbs
|
10
|
+
extras.each do |verb|
|
11
|
+
verb_case = BoundaryCase.new("Verb check with #{verb} for #{endpoint.name}", {}, {})
|
12
|
+
method = ApiTester::ApiMethod.new verb, ApiTester::Response.new, ApiTester::Request.new
|
13
|
+
response = endpoint.call method, verb_case.payload, verb_case.headers
|
14
|
+
test = VerbClass.new response, verb_case.payload, endpoint.not_allowed_response, endpoint.url, verb
|
15
|
+
reports = test.check
|
16
|
+
self.report.reports.concat reports
|
17
|
+
end
|
18
|
+
self.report.reports == []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class VerbClass < MethodCaseTest
|
23
|
+
def initialize response, payload, expected_response, url, verb
|
24
|
+
super response, payload, expected_response, url, verb, "VerbModule"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'api-tester/reporter/status_code_report'
|
2
|
+
require 'api-tester/modules/module'
|
3
|
+
require 'api-tester/method_case_test'
|
4
|
+
|
5
|
+
module ApiTester
|
6
|
+
class Format < Module
|
7
|
+
def go definition, report
|
8
|
+
super
|
9
|
+
|
10
|
+
definition.methods.each do |method|
|
11
|
+
cases = method.request.cases
|
12
|
+
cases.each do |format_case|
|
13
|
+
response = definition.call method, format_case.payload, format_case.headers
|
14
|
+
test = FormatTest.new response, format_case.payload, definition.bad_request_response, definition.url, method.verb
|
15
|
+
self.report.reports.concat test.check
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
report.reports == []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class FormatTest < MethodCaseTest
|
24
|
+
def initialize response, payload, expected_response, url, verb
|
25
|
+
super response, payload, expected_response, url, verb, "FormatModule"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'api-tester/reporter/status_code_report'
|
2
|
+
require 'api-tester/modules/module'
|
3
|
+
require 'api-tester/method_case_test'
|
4
|
+
|
5
|
+
module ApiTester
|
6
|
+
class GoodCase < Module
|
7
|
+
def go endpoint, report
|
8
|
+
super
|
9
|
+
|
10
|
+
endpoint.methods.each do |method|
|
11
|
+
default_case = BoundaryCase.new endpoint.url, method.request.default_payload, method.request.default_headers
|
12
|
+
response = endpoint.call method, default_case.payload, default_case.headers
|
13
|
+
test = GoodCaseTest.new response, endpoint.url, method
|
14
|
+
self.report.reports.concat test.check
|
15
|
+
end
|
16
|
+
|
17
|
+
self.report.reports == []
|
18
|
+
end
|
19
|
+
|
20
|
+
def order
|
21
|
+
1
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class GoodCaseTest < MethodCaseTest
|
27
|
+
def initialize response, url, method
|
28
|
+
super response, method.request.default_payload, method.expected_response, url, method.verb, "GoodCaseModule"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'api-tester/reporter/status_code_report'
|
2
|
+
require 'api-tester/test_helper'
|
3
|
+
|
4
|
+
module ApiTester
|
5
|
+
class Module
|
6
|
+
attr_accessor :report
|
7
|
+
|
8
|
+
def set_report report
|
9
|
+
self.report = report
|
10
|
+
end
|
11
|
+
|
12
|
+
def go definition, report
|
13
|
+
set_report report
|
14
|
+
end
|
15
|
+
|
16
|
+
def order
|
17
|
+
5
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'api-tester/reporter/status_code_report'
|
2
|
+
require 'api-tester/modules/module'
|
3
|
+
require 'api-tester/util/supported_verbs'
|
4
|
+
|
5
|
+
module ApiTester
|
6
|
+
class Typo < Module
|
7
|
+
def go(endpoint, report)
|
8
|
+
super
|
9
|
+
|
10
|
+
allowances(endpoint).each do |verbs|
|
11
|
+
check_typo_url endpoint
|
12
|
+
end
|
13
|
+
|
14
|
+
report.reports == []
|
15
|
+
end
|
16
|
+
|
17
|
+
def check_typo_url endpoint
|
18
|
+
bad_url = "#{endpoint.url}gibberishadsfasdf"
|
19
|
+
bad_endpoint = ApiTester::Endpoint.new "Bad URL", bad_url
|
20
|
+
typo_case = BoundaryCase.new("Typo URL check", {}, {})
|
21
|
+
method = ApiTester::ApiMethod.new ApiTester::SupportedVerbs::GET, ApiTester::Response.new(200), ApiTester::Request.new
|
22
|
+
response = bad_endpoint.call method, typo_case.payload, typo_case.headers
|
23
|
+
|
24
|
+
test = TypoClass.new response, typo_case.payload, endpoint.not_found_response, bad_url, ApiTester::SupportedVerbs::GET
|
25
|
+
reports = test.check
|
26
|
+
self.report.reports.concat reports
|
27
|
+
end
|
28
|
+
|
29
|
+
def allowances(endpoint)
|
30
|
+
allowances = []
|
31
|
+
endpoint.methods.each do |method|
|
32
|
+
allowances << method.verb
|
33
|
+
end
|
34
|
+
allowances.uniq
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class TypoClass < MethodCaseTest
|
39
|
+
def initialize response, payload, expected_response, url, verb
|
40
|
+
super response, payload, expected_response, url, verb, "TypoModule"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'api-tester/reporter/missing_response_field_report'
|
2
|
+
require 'api-tester/modules/module'
|
3
|
+
|
4
|
+
module ApiTester
|
5
|
+
class UnusedFields < Module
|
6
|
+
def go definition, report
|
7
|
+
super
|
8
|
+
|
9
|
+
definition.methods.each do |method|
|
10
|
+
method.expected_response.body.each do |field|
|
11
|
+
if field.is_seen == 0
|
12
|
+
report.add_new_report MissingResponseFieldReport.new(definition.url, method.verb, field.name, "UnusedFieldsModule")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
report.reports == []
|
18
|
+
end
|
19
|
+
|
20
|
+
def order
|
21
|
+
99
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'api-tester/reporter/report'
|
2
|
+
|
3
|
+
module ApiTester
|
4
|
+
class ApiReport
|
5
|
+
attr_accessor :reports
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
self.reports = []
|
9
|
+
end
|
10
|
+
|
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
|
+
end
|
15
|
+
|
16
|
+
def add_new_report report
|
17
|
+
self.reports << report
|
18
|
+
end
|
19
|
+
|
20
|
+
def print
|
21
|
+
if self.reports.size > 0
|
22
|
+
puts "Issues discovered: #{self.reports.size}"
|
23
|
+
self.reports.each do |report|
|
24
|
+
report.print
|
25
|
+
puts "\n"
|
26
|
+
puts "\n"
|
27
|
+
end
|
28
|
+
puts ""
|
29
|
+
puts "Issues discovered: #{self.reports.size}"
|
30
|
+
else
|
31
|
+
puts "No issues found"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module ApiReport
|
2
|
+
class MissingFieldReport
|
3
|
+
attr_accessor :description
|
4
|
+
attr_accessor :url
|
5
|
+
attr_accessor :request
|
6
|
+
attr_accessor :expected_field
|
7
|
+
attr_accessor :actual_response
|
8
|
+
|
9
|
+
def initialize(description, url, request, expected_field)
|
10
|
+
self.description = description
|
11
|
+
self.url = url
|
12
|
+
self.request = request
|
13
|
+
self.expected_field = expected_field
|
14
|
+
self.actual_response = ''
|
15
|
+
end
|
16
|
+
|
17
|
+
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}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|