api-tester 0.0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +182 -0
  8. data/Rakefile +6 -0
  9. data/api-tester.gemspec +39 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/lib/tester.rb +7 -0
  13. data/lib/tester/api_tester.rb +48 -0
  14. data/lib/tester/definition/boundary_case.rb +11 -0
  15. data/lib/tester/definition/endpoint.rb +22 -0
  16. data/lib/tester/definition/fields/array_field.rb +44 -0
  17. data/lib/tester/definition/fields/boolean_field.rb +18 -0
  18. data/lib/tester/definition/fields/email_field.rb +20 -0
  19. data/lib/tester/definition/fields/enum_field.rb +27 -0
  20. data/lib/tester/definition/fields/field.rb +47 -0
  21. data/lib/tester/definition/fields/number_field.rb +17 -0
  22. data/lib/tester/definition/fields/object_field.rb +42 -0
  23. data/lib/tester/definition/methods/api_get.rb +21 -0
  24. data/lib/tester/definition/methods/api_method.rb +22 -0
  25. data/lib/tester/definition/methods/api_post.rb +26 -0
  26. data/lib/tester/definition/request.rb +49 -0
  27. data/lib/tester/definition/response.rb +29 -0
  28. data/lib/tester/method_case_test.rb +67 -0
  29. data/lib/tester/modules/format.rb +26 -0
  30. data/lib/tester/modules/good_case.rb +29 -0
  31. data/lib/tester/modules/module.rb +38 -0
  32. data/lib/tester/modules/typo.rb +67 -0
  33. data/lib/tester/modules/unused_fields.rb +22 -0
  34. data/lib/tester/reporter/api_report.rb +33 -0
  35. data/lib/tester/reporter/missing_field_report.rb +23 -0
  36. data/lib/tester/reporter/missing_response_field_report.rb +19 -0
  37. data/lib/tester/reporter/report.rb +25 -0
  38. data/lib/tester/reporter/status_code_report.rb +12 -0
  39. data/lib/tester/test_helper.rb +7 -0
  40. data/lib/tester/util/response_evaluator.rb +73 -0
  41. data/lib/tester/util/supported_verbs.rb +25 -0
  42. data/lib/tester/version.rb +3 -0
  43. metadata +157 -0
@@ -0,0 +1,18 @@
1
+ require 'tester/definition/fields/field'
2
+
3
+ class BooleanField < Field
4
+ def initialize(name, default_value=true)
5
+ super(name, default_value)
6
+ end
7
+
8
+ def negative_boundary_values
9
+ super +
10
+ [
11
+ "string",
12
+ 123,
13
+ 0,
14
+ 1,
15
+ {}
16
+ ]
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ require 'tester/definition/fields/field'
2
+
3
+ class EmailField < Field
4
+ def initialize(name, default_value="test@test.com")
5
+ super(name, default_value)
6
+ end
7
+
8
+ def negative_boundary_values
9
+ super +
10
+ [
11
+ "string",
12
+ 123,
13
+ 1,
14
+ 0,
15
+ true,
16
+ false,
17
+ {}
18
+ ]
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ require 'tester/definition/fields/field'
2
+
3
+ class EnumField < Field
4
+ attr_accessor :acceptable_values
5
+
6
+ def initialize name, acceptable_values, default_value=nil
7
+ if default_value
8
+ super name, default_value
9
+ else
10
+ super name, acceptable_values[0]
11
+ end
12
+
13
+ self.acceptable_values = acceptable_values
14
+ end
15
+
16
+ def negative_boundary_values
17
+ super +
18
+ [
19
+ 123,
20
+ 0,
21
+ 1,
22
+ true,
23
+ false,
24
+ {}
25
+ ]
26
+ end
27
+ end
@@ -0,0 +1,47 @@
1
+ class Field
2
+ attr_accessor :name
3
+ attr_accessor :default_value
4
+ attr_accessor :required
5
+ attr_accessor :is_seen
6
+
7
+ def initialize name, default_value="string"
8
+ self.name = name
9
+ self.default_value = default_value
10
+ self.required = false
11
+ self.is_seen = 0
12
+ end
13
+
14
+ def is_required
15
+ self.required = true
16
+ self
17
+ end
18
+
19
+ def is_not_required
20
+ self.required = false
21
+ self
22
+ end
23
+
24
+ def has_subfields?
25
+ false
26
+ end
27
+
28
+ def fields
29
+ []
30
+ end
31
+
32
+ def negative_boundary_values
33
+ cases = []
34
+ if self.required
35
+ cases << nil
36
+ end
37
+ cases
38
+ end
39
+
40
+ def seen
41
+ self.is_seen += 1
42
+ end
43
+
44
+ def display_class
45
+ self.class
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ require 'tester/definition/fields/field'
2
+
3
+ class NumberField < Field
4
+ def initialize(name, default_value=5)
5
+ super(name, default_value)
6
+ end
7
+
8
+ def negative_boundary_values
9
+ super +
10
+ [
11
+ "string",
12
+ true,
13
+ false,
14
+ {}
15
+ ]
16
+ end
17
+ end
@@ -0,0 +1,42 @@
1
+ require 'tester/definition/fields/field'
2
+
3
+ class ObjectField < Field
4
+ attr_accessor :fields
5
+
6
+ def initialize name
7
+ super(name)
8
+ self.fields = []
9
+ end
10
+
11
+ def with_field(newField)
12
+ self.fields << newField
13
+ self
14
+ end
15
+
16
+ def has_subfields?
17
+ true
18
+ end
19
+
20
+ def default_value
21
+ obj = Hash.new
22
+
23
+ self.fields.each do |field|
24
+ obj[field.name] = field.default_value
25
+ end
26
+
27
+ obj
28
+ end
29
+
30
+ def negative_boundary_values
31
+ super +
32
+ [
33
+ "string",
34
+ [],
35
+ 123,
36
+ 1,
37
+ 0,
38
+ true,
39
+ false
40
+ ]
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ require 'tester/definition/request'
2
+ require 'tester/definition/response'
3
+ require "tester/version"
4
+ require 'tester/util/supported_verbs'
5
+ require 'rest-client'
6
+ require 'json'
7
+ require 'tester/definition/methods/api_method'
8
+
9
+ class ApiGet < ApiMethod
10
+ def call params={}, headers={}
11
+ headers[:params] = params
12
+
13
+ RestClient.get(self.url, headers) { |real_response, request, result|
14
+ real_response
15
+ }
16
+ end
17
+
18
+ def verb
19
+ SupportedVerbs::GET
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'tester/definition/request'
2
+ require 'tester/definition/response'
3
+ require 'tester/reporter/status_code_report'
4
+ require 'tester/reporter/missing_field_report'
5
+ require 'tester/reporter/api_report'
6
+ require 'json'
7
+
8
+ class ApiMethod
9
+ attr_accessor :request
10
+ attr_accessor :expected_response
11
+ attr_accessor :url
12
+
13
+ def initialize url
14
+ self.url = url
15
+ self.request = Request.new
16
+ self.expected_response = Response.new 200
17
+ end
18
+
19
+ def verb
20
+ "None"
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ require 'tester/definition/request'
2
+ require 'tester/definition/response'
3
+ require "tester/version"
4
+ require 'rest-client'
5
+ require 'json'
6
+ require 'tester/definition/methods/api_method'
7
+ require 'tester/reporter/api_report'
8
+ require 'tester/reporter/status_code_report'
9
+
10
+ class ApiPost < ApiMethod
11
+ attr_accessor :syntax_error_response
12
+
13
+ def post json_payload, headers
14
+ RestClient.post(self.url, json_payload, headers) { |real_response, request, result|
15
+ real_response
16
+ }
17
+ end
18
+
19
+ def call body_params={}, request_params={}
20
+ post body_params.to_json, request_params
21
+ end
22
+
23
+ def verb
24
+ SupportedVerbs::POST
25
+ end
26
+ end
@@ -0,0 +1,49 @@
1
+ require 'tester/definition/boundary_case'
2
+
3
+ class Request
4
+ attr_accessor :definition
5
+ attr_accessor :headers
6
+ attr_accessor :fields
7
+
8
+ def initialize
9
+ self.fields = []
10
+ end
11
+
12
+ def add_field(new_field)
13
+ self.fields << new_field
14
+ self
15
+ end
16
+
17
+ def payload
18
+ response = Hash.new
19
+ self.fields.each do |field|
20
+ response[field.name] = field.default_value
21
+ end
22
+ response
23
+ end
24
+
25
+ def default_payload
26
+ payload
27
+ end
28
+
29
+ def default_headers
30
+ {content_type: :json, accept: :json}
31
+ end
32
+
33
+ def cases
34
+ boundary_cases = Array.new
35
+ self.fields.each do |field|
36
+ field.negative_boundary_values.each do |value|
37
+ bcase = BoundaryCase.new("Setting #{field.name} to #{value}", altered_payload(field.name, value), default_headers)
38
+ boundary_cases.push(bcase)
39
+ end
40
+ end
41
+ boundary_cases
42
+ end
43
+
44
+ def altered_payload field_name, value
45
+ body = payload
46
+ body[field_name] = value
47
+ body
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ class Response
2
+ attr_accessor :code
3
+ attr_accessor :body
4
+
5
+ def initialize(status_code)
6
+ self.code = status_code
7
+ self.body = []
8
+ end
9
+
10
+ def add_field(new_field)
11
+ self.body << new_field
12
+ self
13
+ end
14
+
15
+ def to_s
16
+ s = self.body.map do |f|
17
+ field_display f
18
+ end
19
+ s.to_s
20
+ end
21
+
22
+ def field_display field
23
+ if field.has_subfields?
24
+ "#{field.name}:#{field.fields.map{|f| field_display(f)}}"
25
+ else
26
+ "#{field.name}:#{field.display_class}"
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,67 @@
1
+ require 'tester/util/response_evaluator.rb'
2
+
3
+ class MethodCaseTest
4
+ attr_accessor :expected_response
5
+ attr_accessor :payload
6
+ attr_accessor :response
7
+ attr_accessor :reports
8
+ attr_accessor :url
9
+ attr_accessor :module_name
10
+
11
+ def initialize response, payload, expected_response, url, verb, module_name
12
+ self.payload = payload
13
+ self.response = response
14
+ self.expected_response = expected_response
15
+ self.reports = []
16
+ self.url = "#{verb} #{url}"
17
+ self.module_name = module_name
18
+ end
19
+
20
+ def response_code_report
21
+ report = StatusCodeReport.new "#{module_name} - Incorrect response code", self.url, self.payload, self.expected_response.code, self.response.code
22
+ self.reports << report
23
+ nil
24
+ end
25
+
26
+ def missing_field_report field
27
+ report = Report.new "#{module_name} - Missing field #{field}", self.url, self.payload, self.expected_response, self.response
28
+ self.reports << report
29
+ nil
30
+ end
31
+
32
+ def extra_field_report field
33
+ report = Report.new "#{module_name} - Found extra field #{field}", self.url, self.payload, self.expected_response, self.response
34
+ self.reports << report
35
+ nil
36
+ end
37
+
38
+ def check
39
+ if check_response_code
40
+ evaluator = ResponseEvaluator.new json_parse(self.response.body), self.expected_response
41
+ evaluator.missing_fields.map{|field| missing_field_report(field)}
42
+ evaluator.extra_fields.map{|field| extra_field_report(field)}
43
+ increment_fields evaluator.seen_fields
44
+ end
45
+ return self.reports
46
+ end
47
+
48
+ def check_response_code
49
+ if response.code != expected_response.code
50
+ response_code_report
51
+ return false
52
+ end
53
+ return true
54
+ end
55
+
56
+ def increment_fields seen_fields
57
+ seen_fields.each do |field|
58
+ field.seen
59
+ end
60
+ end
61
+
62
+ def json_parse body
63
+ JSON.parse!(body)
64
+ rescue JSON::ParserError
65
+ body
66
+ end
67
+ end
@@ -0,0 +1,26 @@
1
+ require 'tester/reporter/status_code_report'
2
+ require 'tester/modules/module'
3
+ require 'tester/method_case_test'
4
+
5
+ class Format < Module
6
+ def go definition, report
7
+ super
8
+
9
+ definition.methods.each do |method|
10
+ cases = method.request.cases
11
+ cases.each do |format_case|
12
+ response = self.call method, format_case
13
+ test = FormatTest.new response, format_case.payload, definition.bad_request_response, method.url, method.verb
14
+ self.report.reports.concat test.check
15
+ end
16
+ end
17
+
18
+ report.reports == []
19
+ end
20
+ end
21
+
22
+ class FormatTest < MethodCaseTest
23
+ def initialize response, payload, expected_response, url, verb
24
+ super response, payload, expected_response, url, verb, "FormatModule"
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ require 'tester/reporter/status_code_report'
2
+ require 'tester/modules/module'
3
+ require 'tester/method_case_test'
4
+
5
+ class GoodCase < Module
6
+ def go definition, report
7
+ super
8
+
9
+ definition.methods.each do |method|
10
+ default_case = BoundaryCase.new method.url, method.request.default_payload, method.request.default_headers
11
+ response = self.call method, default_case
12
+ test = GoodCaseTest.new response, method
13
+ self.report.reports.concat test.check
14
+ end
15
+
16
+ self.report.reports == []
17
+ end
18
+
19
+ def order
20
+ 1
21
+ end
22
+ end
23
+
24
+
25
+ class GoodCaseTest < MethodCaseTest
26
+ def initialize response, method
27
+ super response, method.request.default_payload, method.expected_response, method.url, method.verb, "GoodCaseModule"
28
+ end
29
+ end