webspicy 0.16.1 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -5
  3. data/bin/webspicy +6 -1
  4. data/doc/1-black-box-scene.md +109 -0
  5. data/doc/2-black-box-testing.md +27 -0
  6. data/doc/3-specification-importance.md +41 -0
  7. data/doc/4-sequence-diagram.md +82 -0
  8. data/lib/webspicy.rb +15 -4
  9. data/lib/webspicy/configuration.rb +57 -8
  10. data/lib/webspicy/configuration/scope.rb +22 -16
  11. data/lib/webspicy/formaldoc.fio +3 -1
  12. data/lib/webspicy/specification.rb +12 -10
  13. data/lib/webspicy/specification/condition.rb +23 -0
  14. data/lib/webspicy/specification/errcondition.rb +17 -0
  15. data/lib/webspicy/specification/postcondition.rb +1 -0
  16. data/lib/webspicy/specification/precondition.rb +1 -0
  17. data/lib/webspicy/specification/precondition/robust_to_invalid_input.rb +1 -1
  18. data/lib/webspicy/specification/service.rb +38 -25
  19. data/lib/webspicy/specification/test_case.rb +10 -17
  20. data/lib/webspicy/support.rb +22 -0
  21. data/lib/webspicy/support/data_object.rb +25 -0
  22. data/lib/webspicy/support/hooks.rb +65 -0
  23. data/lib/webspicy/support/world.rb +47 -0
  24. data/lib/webspicy/tester.rb +120 -61
  25. data/lib/webspicy/tester/asserter.rb +9 -4
  26. data/lib/webspicy/tester/assertions.rb +8 -9
  27. data/lib/webspicy/tester/client.rb +22 -42
  28. data/lib/webspicy/tester/failure.rb +6 -0
  29. data/lib/webspicy/tester/file_checker.rb +22 -0
  30. data/lib/webspicy/tester/invocation.rb +15 -196
  31. data/lib/webspicy/tester/reporter.rb +85 -0
  32. data/lib/webspicy/tester/reporter/composite.rb +38 -0
  33. data/lib/webspicy/tester/reporter/documentation.rb +67 -0
  34. data/lib/webspicy/tester/reporter/error_count.rb +25 -0
  35. data/lib/webspicy/tester/reporter/exceptions.rb +60 -0
  36. data/lib/webspicy/tester/reporter/file_progress.rb +22 -0
  37. data/lib/webspicy/tester/reporter/file_summary.rb +42 -0
  38. data/lib/webspicy/tester/reporter/progress.rb +28 -0
  39. data/lib/webspicy/tester/reporter/summary.rb +62 -0
  40. data/lib/webspicy/tester/result.rb +139 -0
  41. data/lib/webspicy/tester/result/assert_met.rb +29 -0
  42. data/lib/webspicy/tester/result/check.rb +33 -0
  43. data/lib/webspicy/tester/result/errcondition_met.rb +29 -0
  44. data/lib/webspicy/tester/result/error_schema_met.rb +24 -0
  45. data/lib/webspicy/tester/result/output_schema_met.rb +24 -0
  46. data/lib/webspicy/tester/result/postcondition_met.rb +29 -0
  47. data/lib/webspicy/tester/result/response_header_met.rb +43 -0
  48. data/lib/webspicy/tester/result/response_status_met.rb +25 -0
  49. data/lib/webspicy/version.rb +2 -2
  50. data/lib/webspicy/web.rb +4 -0
  51. data/lib/webspicy/web/client.rb +15 -0
  52. data/lib/webspicy/{tester → web}/client/http_client.rb +34 -14
  53. data/lib/webspicy/{tester → web}/client/rack_test_client.rb +3 -3
  54. data/lib/webspicy/{tester → web}/client/support.rb +2 -2
  55. data/lib/webspicy/web/invocation.rb +65 -0
  56. data/lib/webspicy/web/mocker.rb +90 -0
  57. data/lib/webspicy/web/mocker/config.ru +6 -0
  58. data/lib/webspicy/{openapi.rb → web/openapi.rb} +0 -0
  59. data/lib/webspicy/web/openapi/generator.rb +129 -0
  60. data/spec/unit/configuration/scope/test_each_service.rb +2 -2
  61. data/spec/unit/configuration/scope/test_each_specification.rb +7 -7
  62. data/spec/unit/specification/test_condition.rb +26 -0
  63. data/spec/unit/support/hooks/test_fire_after_each.rb +53 -0
  64. data/spec/unit/{tester/client/test_around.rb → support/hooks/test_fire_around.rb} +15 -10
  65. data/spec/unit/support/hooks/test_fire_before_each.rb +53 -0
  66. data/spec/unit/support/world/fixtures/array.json +8 -0
  67. data/spec/unit/support/world/fixtures/queue.rb +1 -0
  68. data/spec/unit/support/world/fixtures/single.json +11 -0
  69. data/spec/unit/support/world/fixtures/yaml.yml +3 -0
  70. data/spec/unit/support/world/test_world.rb +56 -0
  71. data/spec/unit/test_configuration.rb +50 -1
  72. data/spec/unit/tester/test_asserter.rb +198 -3
  73. data/spec/unit/tester/test_assertions.rb +8 -6
  74. data/spec/unit/web/mocker/test_mocker.rb +35 -0
  75. data/spec/unit/web/openapi/test_generator.rb +31 -0
  76. metadata +72 -61
  77. data/examples/restful/Gemfile +0 -5
  78. data/examples/restful/Gemfile.lock +0 -105
  79. data/examples/restful/Rakefile +0 -25
  80. data/examples/restful/app.rb +0 -180
  81. data/examples/restful/webspicy/config.rb +0 -23
  82. data/examples/restful/webspicy/rack.rb +0 -7
  83. data/examples/restful/webspicy/real.rb +0 -8
  84. data/examples/restful/webspicy/schema.fio +0 -20
  85. data/examples/restful/webspicy/support/must_be_an_admin.rb +0 -20
  86. data/examples/restful/webspicy/support/must_be_authenticated.rb +0 -48
  87. data/examples/restful/webspicy/support/todo_removed.rb +0 -18
  88. data/examples/restful/webspicy/todo/deleteTodo.yml +0 -52
  89. data/examples/restful/webspicy/todo/getTodo.yml +0 -50
  90. data/examples/restful/webspicy/todo/getTodoSingleServiceFormat.yml +0 -46
  91. data/examples/restful/webspicy/todo/getTodos.yml +0 -36
  92. data/examples/restful/webspicy/todo/options.yml +0 -32
  93. data/examples/restful/webspicy/todo/patchTodo.yml +0 -66
  94. data/examples/restful/webspicy/todo/postCsv.yml +0 -43
  95. data/examples/restful/webspicy/todo/postFile.yml +0 -40
  96. data/examples/restful/webspicy/todo/postTodos.yml +0 -51
  97. data/examples/restful/webspicy/todo/putTodo.yml +0 -65
  98. data/examples/restful/webspicy/todo/todos.csv +0 -4
  99. data/examples/single_spec/spec.yml +0 -59
  100. data/examples/website/config.rb +0 -2
  101. data/examples/website/schema.fio +0 -1
  102. data/examples/website/specification/get-http.yml +0 -34
  103. data/examples/website/specification/get-https.yml +0 -34
  104. data/lib/webspicy/checker.rb +0 -25
  105. data/lib/webspicy/mocker.rb +0 -88
  106. data/lib/webspicy/openapi/generator.rb +0 -127
  107. data/lib/webspicy/tester/rspec_asserter.rb +0 -108
  108. data/lib/webspicy/tester/rspec_matchers.rb +0 -104
  109. data/spec/unit/mocker/test_mocker.rb +0 -32
  110. data/spec/unit/openapi/test_generator.rb +0 -28
@@ -0,0 +1,6 @@
1
+ module Webspicy
2
+ class Tester
3
+ class Failure < Exception
4
+ end # class Failure
5
+ end # class Tester
6
+ end # module Webspicy
@@ -0,0 +1,22 @@
1
+ module Webspicy
2
+ class Tester
3
+ class FileChecker < Tester
4
+
5
+ def default_reporter
6
+ @reporter = Reporter::Composite.new
7
+ @reporter << Reporter::FileProgress.new
8
+ @reporter << Reporter::Exceptions.new
9
+ @reporter << Reporter::FileSummary.new
10
+ @reporter << Reporter::ErrorCount.new
11
+ end
12
+
13
+ def run_scope
14
+ scope.each_specification_file do |spec_file|
15
+ @specification = load_specification(spec_file)
16
+ reporter.spec_file_done
17
+ end
18
+ end
19
+
20
+ end # class FileChecker
21
+ end # class Tester
22
+ end # module Webspicy
@@ -1,218 +1,37 @@
1
1
  module Webspicy
2
2
  class Tester
3
3
  class Invocation
4
+ extend Forwardable
4
5
 
5
6
  def initialize(test_case, response, client)
6
7
  @test_case = test_case
7
8
  @response = response
8
9
  @client = client
9
10
  end
10
-
11
11
  attr_reader :test_case, :response, :client
12
12
 
13
- def service
14
- test_case.service
15
- end
16
-
17
- def rspec_assert!(rspec)
18
- RSpecAsserter.new(rspec, self).assert!
19
- end
20
-
21
- def errors
22
- @errors ||= begin
23
- errs = [
24
- [:expected_status_unmet, true],
25
- [:expected_content_type_unmet, !test_case.is_expected_status?(204)],
26
- [:expected_headers_unmet, test_case.has_expected_headers?],
27
- [:expected_schema_unmet, !test_case.is_expected_status?(204)],
28
- [:assertions_unmet, test_case.has_assertions?],
29
- [:postconditions_unmet, test_case.service.has_postconditions? && !test_case.counterexample?],
30
- [:expected_error_unmet, test_case.has_expected_error?]
31
- ].map do |(expectation,only_if)|
32
- next unless only_if
33
- begin
34
- self.send(expectation)
35
- rescue => ex
36
- ex.message
37
- end
38
- end
39
- errs.compact
40
- end
41
- end
42
-
43
- def has_error?
44
- !errors.empty?
45
- end
46
-
47
- ### Getters on response
48
-
49
- def response_code
50
- code = response.status
51
- code = code.code unless code.is_a?(Integer)
52
- code
53
- end
54
-
55
- ### Query methods
56
-
57
- def done?
58
- !response.nil?
59
- end
60
-
61
- def is_expected_success?
62
- test_case.expected_status.to_i >= 200 && test_case.expected_status.to_i < 300
63
- end
64
-
65
- def is_success?
66
- response_code >= 200 && response_code < 300
67
- end
68
-
69
- def is_empty_response?
70
- response_code == 204
71
- end
72
-
73
- def is_redirect?
74
- response_code >= 300 && response_code < 400
75
- end
76
-
77
- ### Check of HTTP status
78
-
79
- def expected_status_unmet
80
- expected = test_case.expected_status
81
- got = response.status
82
- expected === got ? nil : "[status] #{expected} !== #{got}"
83
- end
84
-
85
- def meets_expected_status?
86
- expected_status_unmet.nil?
87
- end
88
-
89
- ### Check of the expected output type
90
-
91
- def expected_content_type_unmet
92
- ect = test_case.expected_content_type
93
- return nil unless ect
94
- got = response.content_type
95
- got = got.mime_type if got.respond_to?(:mime_type)
96
- if ect.nil?
97
- got.nil? ? nil : "[content type] #{ect} != #{got}"
98
- else
99
- got.to_s.start_with?(ect.to_s) ? nil : "[content type] #{ect} != #{got}"
100
- end
101
- end
102
-
103
- def meets_expected_content_type?
104
- expected_content_type_unmet.nil?
105
- end
106
-
107
- ### Check of output schema
108
-
109
- def expected_schema_unmet
110
- if is_empty_response?
111
- body = response.body.to_s.strip
112
- body.empty? ? nil : "[body] empty vs. #{body}"
113
- elsif is_redirect?
114
- else
115
- case dressed_body
116
- when Finitio::TypeError
117
- rc = dressed_body.root_cause
118
- "#{rc.message} (#{rc.location ? rc.location : 'unknown location'})"
119
- when StandardError
120
- dressed_body.message
121
- else nil
122
- end
123
- end
124
- end
125
-
126
- def meets_expected_schema?
127
- expected_schema_unmet.nil?
128
- end
129
-
130
- ### Check of assertions
131
-
132
- def assertions_unmet
133
- unmet = []
134
- asserter = Tester::Asserter.new(dressed_body)
135
- test_case.assert.each do |assert|
136
- begin
137
- asserter.instance_eval(assert)
138
- rescue => ex
139
- unmet << ex.message
140
- end
141
- end
142
- unmet.empty? ? nil : unmet.join("\n")
143
- end
144
-
145
- def value_equal(exp, got)
146
- case exp
147
- when Hash
148
- exp.all?{|(k,v)|
149
- got[k] == v
150
- }
151
- else
152
- exp == got
153
- end
154
- end
155
-
156
- ### Check of expected error message
157
-
158
- def expected_error_unmet
159
- expected = test_case.expected_error
160
- case test_case.expected_content_type
161
- when %r{json}
162
- got = meets_expected_schema? ? dressed_body[:description] : response.body
163
- expected == got ? nil : "[error message] `#{expected}` vs. `#{got}`"
164
- else
165
- dressed_body.include?(expected) ? nil : "[error message] `#{expected}` not found" unless expected.nil?
166
- end
167
- end
168
-
169
- ### Check of expected headers
170
-
171
- def expected_headers_unmet
172
- unmet = []
173
- expected = test_case.expected_headers
174
- expected.each_pair do |k,v|
175
- got = response.headers[k]
176
- unmet << "[headers] #{v} expected for #{k}, got #{got}" unless (got == v)
177
- end
178
- unmet.empty? ? nil : unmet.join("\n")
179
- end
13
+ def_delegators :@client, *[
14
+ :config
15
+ ]
180
16
 
181
- ### Check of postconditions
17
+ def_delegators :@test_case, *[
18
+ :scope,
19
+ :specification,
20
+ :service
21
+ ]
182
22
 
183
- def postconditions_unmet
184
- failures = service.postconditions.map{|post|
185
- post.check(self)
186
- }.compact
187
- failures.empty? ? nil : failures.join("\n")
23
+ def raw_output
24
+ raise NotImplementedError
188
25
  end
189
26
 
190
- def loaded_body
191
- case test_case.expected_content_type
192
- when %r{json}
193
- raise "Body empty while expected" if response.body.to_s.empty?
194
- @loaded_body ||= ::JSON.parse(response.body)
195
- else
196
- response.body.to_s
197
- end
27
+ def loaded_output
28
+ raise NotImplementedError
198
29
  end
199
30
 
200
- def dressed_body
201
- @dressed_body ||= case test_case.expected_content_type
202
- when %r{json}
203
- schema = is_expected_success? ? service.output_schema : service.error_schema
204
- begin
205
- schema.dress(loaded_body)
206
- rescue Finitio::TypeError => ex
207
- ex
208
- end
209
- else
210
- loaded_body
211
- end
31
+ def output
32
+ raise NotImplementedError
212
33
  end
213
34
 
214
35
  end # class Invocation
215
36
  end # class Tester
216
37
  end # module Webspicy
217
- require_relative 'rspec_matchers'
218
- require_relative 'rspec_asserter'
@@ -0,0 +1,85 @@
1
+ module Webspicy
2
+ class Tester
3
+ class Reporter
4
+ include Support::Colorize
5
+
6
+ def initialize(io = STDOUT)
7
+ @io = io
8
+ end
9
+ attr_reader :io
10
+
11
+ def init(tester)
12
+ @tester = tester
13
+ end
14
+ attr_reader :tester
15
+
16
+ [
17
+ :config,
18
+ :scope,
19
+ :client,
20
+ :spec_file,
21
+ :specification,
22
+ :service,
23
+ :test_case,
24
+ :invocation,
25
+ :result
26
+ ].each do |meth|
27
+ define_method(meth) do |*args, &bl|
28
+ tester.send(meth, *args, &bl)
29
+ end
30
+ end
31
+
32
+ HOOKS = [
33
+ :before_all,
34
+ :before_all_done,
35
+ :before_scope,
36
+ :scope_done,
37
+ :before_spec_file,
38
+ :spec_file_error,
39
+ :spec_file_done,
40
+ :before_specification,
41
+ :specification_done,
42
+ :before_service,
43
+ :service_done,
44
+ :before_test_case,
45
+ :test_case_done,
46
+ :before_each,
47
+ :before_each_done,
48
+ :before_instrument,
49
+ :instrument_done,
50
+ :before_invocation,
51
+ :invocation_done,
52
+ :before_assertions,
53
+ :check_success,
54
+ :check_failure,
55
+ :check_error,
56
+ :assertions_done,
57
+ :after_each,
58
+ :after_each_done,
59
+ :after_all,
60
+ :after_all_done,
61
+ :report
62
+ ]
63
+
64
+ HOOKS.each do |meth|
65
+ define_method(meth){|*args, &bl|
66
+ }
67
+ end
68
+
69
+ protected
70
+
71
+ def plural(word, count)
72
+ "%d #{word}#{count>1 ? 's' : ''}" % [count]
73
+ end
74
+
75
+ end # class Reporter
76
+ end # class Tester
77
+ end # module Webspicy
78
+ require_relative 'reporter/error_count'
79
+ require_relative 'reporter/progress'
80
+ require_relative 'reporter/summary'
81
+ require_relative 'reporter/documentation'
82
+ require_relative 'reporter/exceptions'
83
+ require_relative 'reporter/composite'
84
+ require_relative 'reporter/file_progress'
85
+ require_relative 'reporter/file_summary'
@@ -0,0 +1,38 @@
1
+ module Webspicy
2
+ class Tester
3
+ class Reporter
4
+ class Composite < Reporter
5
+
6
+ def initialize(*args, &bl)
7
+ super
8
+ @reporters = []
9
+ end
10
+
11
+ def <<(reporter)
12
+ @reporters << reporter
13
+ self
14
+ end
15
+
16
+ def init(tester)
17
+ super
18
+ @reporters.each do |r|
19
+ r.init(tester)
20
+ end
21
+ end
22
+
23
+ HOOKS.each do |meth|
24
+ define_method(meth) do |*args, &bl|
25
+ @reporters.each do |r|
26
+ r.send(meth, *args, &bl)
27
+ end
28
+ end
29
+ end
30
+
31
+ def find(kind)
32
+ @reporters.find{|r| r.is_a?(kind) }
33
+ end
34
+
35
+ end # class Composite
36
+ end # class Reporter
37
+ end # class Tester
38
+ end # module Webspicy
@@ -0,0 +1,67 @@
1
+ module Webspicy
2
+ class Tester
3
+ class Reporter
4
+ class Documentation < Reporter
5
+
6
+ module Helpers
7
+ def spec_file_error_line(spec_file, ex)
8
+ str = colorize_highlight(spec_file.to_s)
9
+ str += "\n " + colorize_error("X #{ex.message}")
10
+ if ex.root_cause && ex.root_cause != ex
11
+ str += "\n " + colorize_error("#{ex.root_cause.message}")
12
+ end
13
+ str
14
+ end
15
+
16
+ def service_line(service, test_case)
17
+ str = service.to_s + ", " + test_case.to_s
18
+ str = colorize_highlight(str)
19
+ end
20
+
21
+ def check_success_line(check)
22
+ " " + colorize_success("v") + " " + check.behavior
23
+ end
24
+
25
+ def check_failure_line(check, ex)
26
+ " " + colorize_error("F " + ex.message)
27
+ end
28
+
29
+ def check_error_line(check, ex)
30
+ " " + colorize_error("E " + ex.message)
31
+ end
32
+ end
33
+ include Helpers
34
+
35
+ def spec_file_error(e)
36
+ io.puts spec_file_error_line(spec_file, e)
37
+ io.puts
38
+ end
39
+
40
+ def before_test_case
41
+ io.puts service_line(service, test_case)
42
+ end
43
+
44
+ def check_success(check)
45
+ io.puts check_success_line(check)
46
+ end
47
+
48
+ def check_failure(check, ex)
49
+ io.puts check_failure_line(check, ex)
50
+ end
51
+
52
+ def check_error(check, ex)
53
+ io.puts check_error_line(check, ex)
54
+ end
55
+
56
+ def test_case_done
57
+ io.puts
58
+ end
59
+
60
+ def service_done
61
+ io.puts
62
+ end
63
+
64
+ end # class Documentation
65
+ end # class Reporter
66
+ end # class Tester
67
+ end # module Webspicy