webspicy 0.16.3 → 0.17.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -3
  3. data/examples/restful/Gemfile.lock +14 -13
  4. data/examples/restful/webspicy/config.rb +1 -0
  5. data/examples/restful/webspicy/{todo/deleteTodo.yml → formaldef/todo/_one/delete.yml} +3 -0
  6. data/examples/restful/webspicy/{todo/getTodoSingleServiceFormat.yml → formaldef/todo/_one/get.simpler.yml} +0 -0
  7. data/examples/restful/webspicy/{todo/getTodo.yml → formaldef/todo/_one/get.yml} +0 -0
  8. data/examples/restful/webspicy/{todo/patchTodo.yml → formaldef/todo/_one/patch.yml} +0 -0
  9. data/examples/restful/webspicy/{todo/putTodo.yml → formaldef/todo/_one/put.yml} +0 -0
  10. data/examples/restful/webspicy/{todo/getTodos.yml → formaldef/todo/get.yml} +0 -0
  11. data/examples/restful/webspicy/{todo → formaldef/todo}/options.yml +0 -0
  12. data/examples/restful/webspicy/{todo/postCsv.yml → formaldef/todo/post.csv.yml} +0 -0
  13. data/examples/restful/webspicy/{todo/postFile.yml → formaldef/todo/post.file.yml} +0 -0
  14. data/examples/restful/webspicy/{todo/postTodos.yml → formaldef/todo/post.yml} +0 -0
  15. data/examples/restful/webspicy/{todo → formaldef/todo}/todos.csv +0 -0
  16. data/examples/restful/webspicy/support/todo_not_removed.rb +21 -0
  17. data/examples/restful/webspicy/support/todo_removed.rb +5 -3
  18. data/lib/webspicy.rb +1 -0
  19. data/lib/webspicy/checker.rb +5 -20
  20. data/lib/webspicy/configuration.rb +9 -0
  21. data/lib/webspicy/configuration/scope.rb +0 -8
  22. data/lib/webspicy/formaldoc.fio +2 -0
  23. data/lib/webspicy/rspec/checker.rb +2 -0
  24. data/lib/webspicy/rspec/checker/rspec_checker.rb +24 -0
  25. data/lib/webspicy/rspec/support/rspec_runnable.rb +27 -0
  26. data/lib/webspicy/rspec/tester.rb +4 -0
  27. data/lib/webspicy/{tester → rspec/tester}/rspec_asserter.rb +23 -10
  28. data/lib/webspicy/{tester → rspec/tester}/rspec_matchers.rb +10 -0
  29. data/lib/webspicy/rspec/tester/rspec_tester.rb +63 -0
  30. data/lib/webspicy/specification.rb +5 -7
  31. data/lib/webspicy/specification/errcondition.rb +16 -0
  32. data/lib/webspicy/specification/service.rb +27 -19
  33. data/lib/webspicy/specification/test_case.rb +3 -9
  34. data/lib/webspicy/support.rb +1 -0
  35. data/lib/webspicy/support/data_object.rb +25 -0
  36. data/lib/webspicy/tester.rb +4 -78
  37. data/lib/webspicy/tester/asserter.rb +9 -4
  38. data/lib/webspicy/tester/assertions.rb +8 -9
  39. data/lib/webspicy/tester/failure.rb +6 -0
  40. data/lib/webspicy/tester/invocation.rb +8 -156
  41. data/lib/webspicy/version.rb +2 -2
  42. data/spec/unit/configuration/scope/test_each_service.rb +2 -2
  43. data/spec/unit/configuration/scope/test_each_specification.rb +7 -7
  44. data/spec/unit/test_configuration.rb +1 -1
  45. data/spec/unit/tester/test_asserter.rb +198 -3
  46. data/spec/unit/tester/test_assertions.rb +8 -6
  47. metadata +27 -18
@@ -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
@@ -14,36 +14,6 @@ module Webspicy
14
14
  test_case.service
15
15
  end
16
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
17
  ### Getters on response
48
18
 
49
19
  def response_code
@@ -74,121 +44,10 @@ module Webspicy
74
44
  response_code >= 300 && response_code < 400
75
45
  end
76
46
 
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
180
-
181
- ### Check of postconditions
182
-
183
- def postconditions_unmet
184
- failures = service.postconditions.map{|post|
185
- post.check(self)
186
- }.compact
187
- failures.empty? ? nil : failures.join("\n")
188
- end
189
-
190
47
  def loaded_body
191
- case test_case.expected_content_type
48
+ ct = response.content_type || test_case.expected_content_type
49
+ ct = ct.mime_type if ct.respond_to?(:mime_type)
50
+ case ct
192
51
  when %r{json}
193
52
  raise "Body empty while expected" if response.body.to_s.empty?
194
53
  @loaded_body ||= ::JSON.parse(response.body)
@@ -198,21 +57,14 @@ module Webspicy
198
57
  end
199
58
 
200
59
  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
60
+ schema = is_expected_success? ? service.output_schema : service.error_schema
61
+ begin
62
+ schema.dress(loaded_body)
63
+ rescue Finitio::TypeError => ex
64
+ ex
211
65
  end
212
66
  end
213
67
 
214
68
  end # class Invocation
215
69
  end # class Tester
216
70
  end # module Webspicy
217
- require_relative 'rspec_matchers'
218
- require_relative 'rspec_asserter'
@@ -1,8 +1,8 @@
1
1
  module Webspicy
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 16
5
- TINY = 3
4
+ MINOR = 17
5
+ TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
@@ -19,7 +19,7 @@ module Webspicy
19
19
 
20
20
  let(:configuration) {
21
21
  Configuration.new(restful_folder){|c|
22
- c.file_filter = /getTodo.yml/
22
+ c.file_filter = /get.yml/
23
23
  }
24
24
  }
25
25
 
@@ -32,7 +32,7 @@ module Webspicy
32
32
 
33
33
  let(:configuration) {
34
34
  Configuration.new(restful_folder){|c|
35
- c.file_filter = /getTodo.yml/
35
+ c.file_filter = /get.yml/
36
36
  c.service_filter = ->(s) {
37
37
  s.method == "POST"
38
38
  }
@@ -27,13 +27,13 @@ module Webspicy
27
27
  let(:configuration) {
28
28
  Configuration.new(restful_folder){|c|
29
29
  c.file_filter = ->(f) {
30
- f.basename.to_s == "getTodo.yml"
30
+ f.basename.to_s == "get.yml"
31
31
  }
32
32
  }
33
33
  }
34
34
 
35
- it 'returns only that file' do
36
- expect(subject.size).to eql(1)
35
+ it 'returns only that files' do
36
+ expect(subject.size).to eql(2)
37
37
  end
38
38
  end
39
39
 
@@ -41,12 +41,12 @@ module Webspicy
41
41
 
42
42
  let(:configuration) {
43
43
  Configuration.new(restful_folder){|c|
44
- c.file_filter = /getTodo.yml/
44
+ c.file_filter = /get.yml/
45
45
  }
46
46
  }
47
47
 
48
- it 'returns only that file' do
49
- expect(subject.size).to eql(1)
48
+ it 'returns only that files' do
49
+ expect(subject.size).to eql(2)
50
50
  end
51
51
  end
52
52
 
@@ -54,7 +54,7 @@ module Webspicy
54
54
 
55
55
  let(:configuration) {
56
56
  Configuration.new(restful_folder) do |c|
57
- c.folder 'todo'
57
+ c.folder 'formaldef/todo'
58
58
  end
59
59
  }
60
60
 
@@ -40,7 +40,7 @@ module Webspicy
40
40
  end
41
41
 
42
42
  it 'supports a single .yml file and returns a specific configuration instance' do
43
- file = restful_folder/"todo/getTodo.yml"
43
+ file = restful_folder/"formaldef/todo/get.yml"
44
44
  c = Configuration.dress(file)
45
45
  expect(c).to be_a(Configuration)
46
46
  expect(c.folder).to eq(restful_folder)
@@ -3,10 +3,75 @@ require 'spec_helper'
3
3
  module Webspicy
4
4
  class Tester
5
5
  describe Asserter do
6
- let(:asserter) { Asserter.new(target) }
6
+ describe '#includes' do
7
+ it 'returns nil when the value is found in an array' do
8
+ expect(Asserter.new([:a, 'foo', 99]).includes('', 'foo'))
9
+ .to eq nil
10
+ end
11
+
12
+ it 'returns nil when the assertion is identical to a non-array' do
13
+ expect(Asserter.new({ foo: Time.at(0) }).includes('', { foo: Time.at(0) }))
14
+ .to eq nil
15
+ end
16
+
17
+ it 'raises an exception with a descriptive message when the assertion is false' do
18
+ expect { Asserter.new([:a, 'foo', 99]).includes('', 42) }
19
+ .to raise_exception Failure,
20
+ 'Expected ["a","foo",99] to include 42'
21
+
22
+ end
23
+ end
24
+
25
+ describe '#notIncludes' do
26
+ it 'returns nil when the value is not found in an array' do
27
+ expect(Asserter.new([:a, 'foo', 99]).notIncludes('', 'boom'))
28
+ .to eq nil
29
+ end
30
+
31
+ it 'returns nil when the assertion and target are not identical' do
32
+ expect(Asserter.new({ foo: Time.at(0) }).notIncludes('', 'LOL'))
33
+ .to eq nil
34
+ end
35
+
36
+ it 'raises an exception with a descriptive message when the assertion is false' do
37
+ expect { Asserter.new([:a, 'foo', 99]).notIncludes('', 'foo') }
38
+ .to raise_exception Failure,
39
+ 'Expected ["a","foo",99] not to include foo'
40
+ end
41
+ end
42
+
43
+ describe '#empty' do
44
+ it 'returns nil when the target is empty' do
45
+ expect(Asserter.new([]).empty).to eq nil
46
+ expect(Asserter.new({}).empty).to eq nil
47
+ end
48
+
49
+ it 'raises an exception with a descriptive message when the assertion is false' do
50
+ expect { Asserter.new(['foo']).empty }
51
+ .to raise_exception Failure,
52
+ 'Expected ["foo"] to be empty'
53
+ end
54
+ end
55
+
56
+ describe '#notEmpty' do
57
+ it 'returns nil when the target is not empty' do
58
+ expect(Asserter.new(['foo']).notEmpty).to eq nil
59
+ expect(Asserter.new({ foo: 'bar' }).notEmpty).to eq nil
60
+ end
61
+
62
+ it 'raises an exception with a descriptive message when the assertion is false' do
63
+ expect { Asserter.new([]).notEmpty }
64
+ .to raise_exception Failure,
65
+ 'Expected [] to be non empty'
66
+ expect { Asserter.new({}).notEmpty }
67
+ .to raise_exception Failure,
68
+ 'Expected {} to be non empty'
69
+ end
70
+ end
7
71
 
8
72
  describe '#size' do
9
73
  let(:target) { [1, 2, 3, 4] }
74
+ let(:asserter) { Asserter.new(target) }
10
75
 
11
76
  it 'returns nil when the assertion is true for a plain array' do
12
77
  expect(asserter.size('', 4)).to eq nil
@@ -14,7 +79,7 @@ module Webspicy
14
79
 
15
80
  it 'raises an exception with a descriptive message when the assertion is false' do
16
81
  expect { asserter.size('', 3) }
17
- .to raise_exception RuntimeError, 'Expected [1,2,3,4] to have a size of 3, actual size is: 4'
82
+ .to raise_exception Failure, 'Expected [1,2,3,4] to have a size of 3, actual size is: 4'
18
83
  end
19
84
 
20
85
  context 'with a string' do
@@ -34,7 +99,7 @@ module Webspicy
34
99
 
35
100
  it 'raises an exception with a descriptive message when the assertion is false' do
36
101
  expect { asserter.size('foo/2/bar', 99) }
37
- .to raise_exception RuntimeError,
102
+ .to raise_exception Failure,
38
103
  'Expected [10,11,12] to have a size of 99, actual size is: 3'
39
104
  end
40
105
 
@@ -45,6 +110,136 @@ module Webspicy
45
110
  end
46
111
  end
47
112
  end
113
+
114
+ describe '#idIn' do
115
+ it 'returns nil when the specified ids match exactly' do
116
+ expect(Asserter.new([{ id: 1 }, { id: 2 }]).idIn('', 1, 2)).to eq nil
117
+ expect(Asserter.new({ id: 123 }).idIn('', 123)).to eq nil
118
+
119
+ os = OpenStruct.new(id: 'foo')
120
+ expect(Asserter.new(os).idIn('', 'foo')).to eq nil
121
+ end
122
+
123
+ it 'raises an exception with a descriptive message when the assertion is false' do
124
+ expect { Asserter.new([{ id: 1}, { id: 2}]).idIn('', 123, 125) }
125
+ .to raise_exception Failure,
126
+ 'Expected [{"id":1},{"id":2}] to have ids 123,125'
127
+ expect { Asserter.new([{ id: 1}, { id: 2}]).idIn('', 1, 2, 3) }
128
+ .to raise_exception Failure,
129
+ 'Expected [{"id":1},{"id":2}] to have ids 1,2,3'
130
+ expect { Asserter.new([{ id: 1}, { id: 2}]).idIn('', 1) }
131
+ .to raise_exception Failure,
132
+ 'Expected [{"id":1},{"id":2}] to have ids 1'
133
+ end
134
+ end
135
+
136
+ describe '#idNotIn' do
137
+ it 'returns nil when the specified ids do not match exactly' do
138
+ expect(Asserter.new([{ id: 1 }, { id: 2 }]).idNotIn('', 123, 125)).to eq nil
139
+ expect(Asserter.new({ id: 123 }).idNotIn('', 1, 2, 3)).to eq nil
140
+ expect(Asserter.new({ id: 123 }).idNotIn('', 1)).to eq nil
141
+ end
142
+
143
+ it 'raises an exception with a descriptive message when the assertion is false' do
144
+ expect { Asserter.new([{ id: 1}, { id: 2}]).idNotIn('', 1, 2) }
145
+ .to raise_exception Failure,
146
+ 'Expected [{"id":1},{"id":2}] to not have ids 1,2'
147
+ expect { Asserter.new({ id: 1}).idNotIn('', 1) }
148
+ .to raise_exception Failure,
149
+ 'Expected {"id":1} to not have ids 1'
150
+
151
+ os = OpenStruct.new(id: 'foo')
152
+ expect { Asserter.new(os).idNotIn('', 'foo') }
153
+ .to raise_exception Failure,
154
+ 'Expected "#<OpenStruct id=\"foo\">"... to not have ids foo'
155
+ end
156
+ end
157
+ end
158
+
159
+ describe '#idFD' do
160
+ let(:target) do
161
+ [
162
+ { id: 1, a: 'a1', b: 'b1' },
163
+ { id: 2, a: 'a2', b: 'b2' }
164
+ ]
165
+ end
166
+
167
+ it 'returns nil when the element with specified id matches the expected keys-value pairs' do
168
+ expect(Asserter.new(target).idFD('', 2, { b: 'b2' })).to eq nil
169
+ expect(Asserter.new(target).idFD('', 2, { a: 'a2', b: 'b2' })).to eq nil
170
+ end
171
+
172
+ it 'raises an exception when the assertion is false' do
173
+ expect { Asserter.new(target).idFD('', 2, { c: 'c2' }) }
174
+ .to raise_exception Failure,
175
+ 'Expected [{"id":1,"a":"a1","b":"b1"... ' \
176
+ 'to contain the key(s) and value(s) {:c=>"c2"}'
177
+ expect { Asserter.new(target).idFD('', 2, { b: 'b1' }) }
178
+ .to raise_exception Failure,
179
+ 'Expected [{"id":1,"a":"a1","b":"b1"... ' \
180
+ 'to contain the key(s) and value(s) {:b=>"b1"}'
181
+ end
182
+
183
+ it 'raises an exception with a descriptive message when no element with the specified id is present in target' do
184
+ expect { Asserter.new(target).idFD('', 3, { a: 'a3' }) }
185
+ .to raise_exception Failure,
186
+ 'Expected an element with id 3 to contain ' \
187
+ 'the key(s) and value(s) {:a=>"a3"}, '\
188
+ 'but there is no element with that id'
189
+ end
190
+ end
191
+
192
+ describe '#pathFD' do
193
+ let(:target) do
194
+ [
195
+ { a: 'a1', b: 'b1' },
196
+ { a: 'a2', b: 'b2' }
197
+ ]
198
+ end
199
+
200
+ it 'returns nil when the target matches the expected keys-value pairs' do
201
+ expect(Asserter.new(target).pathFD('0', { b: 'b1' })).to eq nil
202
+ expect(Asserter.new(target).pathFD('0', { a: 'a1', b: 'b1' })).to eq nil
203
+ expect(Asserter.new(target).pathFD('1', { b: 'b2' })).to eq nil
204
+ expect(Asserter.new(target).pathFD('1', { a: 'a2', b: 'b2' })).to eq nil
205
+ end
206
+
207
+ it 'raises an exception when the assertion is false' do
208
+ expect { Asserter.new(target).pathFD('0', { c: 'c2' }) }
209
+ .to raise_exception Failure,
210
+ 'Expected {"a":"a1","b":"b1"} ' \
211
+ 'to contain the key(s) and value(s) {:c=>"c2"}'
212
+ expect { Asserter.new(target).pathFD('0', { b: 'b2' }) }
213
+ .to raise_exception Failure,
214
+ 'Expected {"a":"a1","b":"b1"} ' \
215
+ 'to contain the key(s) and value(s) {:b=>"b2"}'
216
+ end
217
+ end
218
+
219
+ describe '#match' do
220
+ it 'returns nil when the target matches the specified regexp' do
221
+ expect(Asserter.new('Empathise').match('', /path/)).to eq nil
222
+ expect(Asserter.new('Empathise').match('', /.mp/)).to eq nil
223
+ end
224
+
225
+ it 'raises an exception when the assertion is false' do
226
+ expect { Asserter.new('Empathise').match('', /ize/) }
227
+ .to raise_exception Failure,
228
+ 'Expected "Empathise" to match /ize/'
229
+ end
230
+ end
231
+
232
+ describe '#notMatch' do
233
+ it 'returns nil when the target does not matche the specified regexp' do
234
+ expect(Asserter.new('Empathise').notMatch('', /ize/)).to eq nil
235
+ expect(Asserter.new('Empathise').notMatch('', /^path/)).to eq nil
236
+ end
237
+
238
+ it 'raises an exception when the assertion is false' do
239
+ expect { Asserter.new('Empathise').notMatch('', /path/) }
240
+ .to raise_exception Failure,
241
+ 'Expected "Empathise" not to match /path/'
242
+ end
48
243
  end
49
244
  end
50
245
  end