webspicy 0.16.0 → 0.18.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -5
  3. data/examples/restful/webspicy/config.rb +1 -0
  4. data/examples/restful/webspicy/{todo/deleteTodo.yml → formaldef/todo/_one/delete.yml} +3 -0
  5. data/examples/restful/webspicy/{todo/getTodoSingleServiceFormat.yml → formaldef/todo/_one/get.simpler.yml} +0 -0
  6. data/examples/restful/webspicy/{todo/getTodo.yml → formaldef/todo/_one/get.yml} +0 -0
  7. data/examples/restful/webspicy/{todo/patchTodo.yml → formaldef/todo/_one/patch.yml} +0 -0
  8. data/examples/restful/webspicy/{todo/putTodo.yml → formaldef/todo/_one/put.yml} +0 -0
  9. data/examples/restful/webspicy/{todo/getTodos.yml → formaldef/todo/get.yml} +0 -0
  10. data/examples/restful/webspicy/{todo → formaldef/todo}/options.yml +0 -0
  11. data/examples/restful/webspicy/{todo/postCsv.yml → formaldef/todo/post.csv.yml} +0 -0
  12. data/examples/restful/webspicy/{todo/postFile.yml → formaldef/todo/post.file.yml} +0 -0
  13. data/examples/restful/webspicy/{todo/postTodos.yml → formaldef/todo/post.yml} +0 -0
  14. data/examples/restful/webspicy/{todo → formaldef/todo}/todos.csv +0 -0
  15. data/examples/restful/webspicy/support/todo_not_removed.rb +21 -0
  16. data/examples/restful/webspicy/support/todo_removed.rb +5 -3
  17. data/examples/website/specification/get-http.yml +20 -24
  18. data/examples/website/specification/get-https.yml +20 -24
  19. data/lib/webspicy.rb +4 -3
  20. data/lib/webspicy/checker.rb +5 -20
  21. data/lib/webspicy/configuration.rb +9 -0
  22. data/lib/webspicy/configuration/scope.rb +0 -8
  23. data/lib/webspicy/formaldoc.fio +8 -6
  24. data/lib/webspicy/mocker/config.ru +5 -0
  25. data/lib/webspicy/rspec/checker.rb +2 -0
  26. data/lib/webspicy/rspec/checker/rspec_checker.rb +24 -0
  27. data/lib/webspicy/rspec/support/rspec_runnable.rb +27 -0
  28. data/lib/webspicy/rspec/tester.rb +4 -0
  29. data/lib/webspicy/{tester → rspec/tester}/rspec_asserter.rb +24 -11
  30. data/lib/webspicy/{tester → rspec/tester}/rspec_matchers.rb +10 -0
  31. data/lib/webspicy/rspec/tester/rspec_tester.rb +63 -0
  32. data/lib/webspicy/specification.rb +10 -10
  33. data/lib/webspicy/specification/errcondition.rb +16 -0
  34. data/lib/webspicy/specification/precondition/robust_to_invalid_input.rb +1 -1
  35. data/lib/webspicy/specification/service.rb +27 -19
  36. data/lib/webspicy/specification/test_case.rb +3 -9
  37. data/lib/webspicy/support.rb +1 -0
  38. data/lib/webspicy/support/data_object.rb +25 -0
  39. data/lib/webspicy/tester.rb +4 -78
  40. data/lib/webspicy/tester/asserter.rb +9 -4
  41. data/lib/webspicy/tester/assertions.rb +8 -9
  42. data/lib/webspicy/tester/failure.rb +6 -0
  43. data/lib/webspicy/tester/invocation.rb +8 -156
  44. data/lib/webspicy/version.rb +1 -1
  45. data/spec/unit/configuration/scope/test_each_service.rb +2 -2
  46. data/spec/unit/configuration/scope/test_each_specification.rb +7 -7
  47. data/spec/unit/test_configuration.rb +1 -1
  48. data/spec/unit/tester/test_asserter.rb +198 -3
  49. data/spec/unit/tester/test_assertions.rb +8 -6
  50. metadata +33 -25
  51. data/LICENSE.md +0 -22
  52. data/examples/restful/Gemfile.lock +0 -105
@@ -0,0 +1,4 @@
1
+ require_relative 'support/rspec_runnable'
2
+ require_relative 'tester/rspec_matchers'
3
+ require_relative 'tester/rspec_asserter'
4
+ require_relative 'tester/rspec_tester'
@@ -8,6 +8,10 @@ module Webspicy
8
8
  end
9
9
  attr_reader :rspec, :invocation
10
10
 
11
+ def self.call(rspec, invocation)
12
+ new(rspec, invocation).send(:assert!)
13
+ end
14
+
11
15
  def response
12
16
  invocation.response
13
17
  end
@@ -20,15 +24,20 @@ module Webspicy
20
24
  test_case.service
21
25
  end
22
26
 
27
+ protected
28
+
23
29
  def assert!
24
- assert_status_met
25
- assert_content_type_met
26
- assert_expected_headers
30
+ rspec.aggregate_failures do
31
+ assert_status_met
32
+ assert_content_type_met
33
+ assert_expected_headers
34
+ end
27
35
  assert_output_schema_met
28
- assert_assertions_met
29
- assert_postconditions_met
30
-
31
- assert_no_other_errors
36
+ rspec.aggregate_failures do
37
+ assert_assertions_met
38
+ assert_postconditions_met
39
+ assert_errconditions_met
40
+ end
32
41
  end
33
42
 
34
43
  def assert_status_met
@@ -45,7 +54,7 @@ module Webspicy
45
54
  if ect.nil?
46
55
  rspec.expect(ect).to rspec.have_no_response_type
47
56
  else
48
- rspec.expect(ect).to rspec.match_content_type(got)
57
+ rspec.expect(got).to rspec.match_content_type(ect)
49
58
  end
50
59
  end
51
60
 
@@ -98,9 +107,13 @@ module Webspicy
98
107
  end
99
108
  end
100
109
 
101
- def assert_no_other_errors
102
- errors = invocation.errors
103
- rspec.expect(errors).to rspec.be_an_empty_errors_array
110
+ def assert_errconditions_met
111
+ return unless service.has_errconditions?
112
+ return unless test_case.counterexample?
113
+ service.errconditions.each do |post|
114
+ msg = post.check(invocation)
115
+ rspec.expect(msg).to rspec.meet_errcondition(post)
116
+ end
104
117
  end
105
118
 
106
119
  end # class RSpecAsserter
@@ -94,6 +94,16 @@ RSpec::Matchers.define :meet_postcondition do |post|
94
94
  end
95
95
  end
96
96
 
97
+ RSpec::Matchers.define :meet_errcondition do |post|
98
+ match do |actual|
99
+ actual.nil?
100
+ end
101
+ failure_message_for_should do |actual|
102
+ "expected errcondition `#{post.class.name}` to be met, got following error:\n" + \
103
+ " #{actual}"
104
+ end
105
+ end
106
+
97
107
  RSpec::Matchers.define :be_an_empty_errors_array do
98
108
  match do |actual|
99
109
  actual.empty?
@@ -0,0 +1,63 @@
1
+ module Webspicy
2
+ class Tester
3
+ class RSpecTester
4
+ include Webspicy::Support::RSpecRunnable
5
+
6
+ protected
7
+
8
+ def load_rspec_examples
9
+ tester = self
10
+ RSpec.describe "Webspicy test suite" do
11
+ before(:all) do
12
+ tester.config.listeners(:before_all).each do |l|
13
+ l.call(tester.config)
14
+ end
15
+ end
16
+ after(:all) do
17
+ tester.config.listeners(:after_all).each do |l|
18
+ l.call(tester.config)
19
+ end
20
+ end
21
+ tester.config.each_scope do |scope|
22
+ client = scope.get_client
23
+ scope.each_specification do |specification|
24
+ scope.each_service(specification) do |service|
25
+ scope.each_testcase(service) do |test_case|
26
+ str = "#{service} #{test_case}"
27
+ str = Webspicy::Support::Colorize.colorize_highlight(str, tester.config)
28
+ describe(str) do
29
+
30
+ around(:each) do |example|
31
+ client.around(test_case) do
32
+ client.before(test_case)
33
+ test_case.instrument(client)
34
+ client.instrument(test_case)
35
+ @response = client.call(test_case)
36
+ @invocation = Tester::Invocation.new(test_case, @response, client)
37
+ example.run
38
+ client.after(test_case, @invocation)
39
+ @invocation
40
+ end
41
+ end
42
+
43
+ let(:invocation) do
44
+ @invocation
45
+ end
46
+
47
+ it "meets its specification" do
48
+ raise "Test not ran" unless invocation.done?
49
+ RSpecAsserter.call(self, invocation)
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ self
59
+ end
60
+
61
+ end # class RSpecTester
62
+ end # class Tester
63
+ end # module Webspicy
@@ -1,11 +1,13 @@
1
1
  module Webspicy
2
2
  class Specification
3
+ include Support::DataObject
3
4
 
4
5
  def initialize(raw, location = nil)
5
- @raw = raw
6
+ super(raw)
6
7
  @location = location
7
8
  bind_services
8
9
  end
10
+ attr_accessor :config
9
11
  attr_reader :location
10
12
 
11
13
  def self.info(raw)
@@ -14,11 +16,13 @@ module Webspicy
14
16
 
15
17
  def self.singleservice(raw)
16
18
  converted = {
17
- name: raw.has_key?(:name) ? raw[:name] : "Unamed specification",
19
+ name: raw[:name] || "Unamed specification",
18
20
  url: raw[:url],
19
- services: [raw.reject{|k, _| k == :name || k == :url}]
21
+ services: [
22
+ Webspicy.service(raw.reject{|k| k==:url or k==:name }, Webspicy.current_scope)
23
+ ]
20
24
  }
21
- self.info(raw)
25
+ info(converted)
22
26
  end
23
27
 
24
28
  def located_at!(location)
@@ -44,7 +48,7 @@ module Webspicy
44
48
  end
45
49
 
46
50
  def services
47
- @raw[:services]
51
+ @raw[:services] || []
48
52
  end
49
53
 
50
54
  def url_placeholders
@@ -60,10 +64,6 @@ module Webspicy
60
64
  [ url, rest ]
61
65
  end
62
66
 
63
- def to_info
64
- @raw
65
- end
66
-
67
67
  def to_singleservice
68
68
  raise NotImplementedError
69
69
  end
@@ -85,7 +85,7 @@ module Webspicy
85
85
  end
86
86
 
87
87
  def bind_services
88
- (@raw[:services] ||= []).each do |s|
88
+ services.each do |s|
89
89
  s.specification = self
90
90
  end
91
91
  end
@@ -0,0 +1,16 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Errcondition
4
+
5
+ def self.match(service, descr)
6
+ end
7
+
8
+ def instrument(test_case, client)
9
+ end
10
+
11
+ def check(invocation)
12
+ end
13
+
14
+ end # module Errcondition
15
+ end # module Specification
16
+ end # module Webspicy
@@ -40,7 +40,7 @@ module Webspicy
40
40
  def empty_input_counterexamples(service, first)
41
41
  placeholders = service.specification.url_placeholders
42
42
  empty_input = first.params.reject{|k| !placeholders.include?(k) }
43
- if !empty_input.empty? && invalid_input?(service, empty_input)
43
+ if invalid_input?(service, empty_input)
44
44
  [first.mutate({
45
45
  :description => "it is robust to an invalid empty input (RobustToInvalidInput)",
46
46
  :dress_params => false,
@@ -1,13 +1,12 @@
1
1
  module Webspicy
2
2
  class Specification
3
3
  class Service
4
+ include Support::DataObject
4
5
 
5
6
  def initialize(raw)
6
- @raw = raw
7
+ super(raw)
7
8
  bind_examples
8
9
  bind_counterexamples
9
- @preconditions = compile_preconditions
10
- @postconditions = compile_postconditions
11
10
  end
12
11
  attr_accessor :specification
13
12
 
@@ -15,6 +14,10 @@ module Webspicy
15
14
  new(raw)
16
15
  end
17
16
 
17
+ def config
18
+ specification.config
19
+ end
20
+
18
21
  def method
19
22
  @raw[:method]
20
23
  end
@@ -24,7 +27,7 @@ module Webspicy
24
27
  end
25
28
 
26
29
  def preconditions
27
- @preconditions
30
+ @preconditions ||= compile_preconditions
28
31
  end
29
32
 
30
33
  def has_preconditions?
@@ -32,23 +35,31 @@ module Webspicy
32
35
  end
33
36
 
34
37
  def postconditions
35
- @postconditions
38
+ @postconditions ||= compile_postconditions
36
39
  end
37
40
 
38
41
  def has_postconditions?
39
42
  !postconditions.empty?
40
43
  end
41
44
 
45
+ def errconditions
46
+ @errconditions ||= compile_errconditions
47
+ end
48
+
49
+ def has_errconditions?
50
+ !errconditions.empty?
51
+ end
52
+
42
53
  def default_example
43
54
  @raw[:default_example]
44
55
  end
45
56
 
46
57
  def examples
47
- @raw[:examples]
58
+ @raw[:examples] || []
48
59
  end
49
60
 
50
61
  def counterexamples
51
- @raw[:counterexamples]
62
+ @raw[:counterexamples] || []
52
63
  end
53
64
 
54
65
  def generated_counterexamples
@@ -76,28 +87,25 @@ module Webspicy
76
87
  input_schema.dress(params)
77
88
  end
78
89
 
79
- def to_info
80
- @raw
81
- end
82
-
83
90
  def to_s
84
91
  "#{method} #{specification.url}"
85
92
  end
86
93
 
87
94
  private
88
95
 
89
- def scope
90
- Webspicy.current_scope
91
- end
92
-
93
96
  def compile_preconditions
94
97
  @raw[:preconditions] = [@raw[:preconditions]] if @raw[:preconditions].is_a?(String)
95
- compile_conditions(@raw[:preconditions] ||= [], scope.preconditions)
98
+ compile_conditions(@raw[:preconditions] ||= [], config.preconditions)
96
99
  end
97
100
 
98
101
  def compile_postconditions
99
102
  @raw[:postconditions] = [@raw[:postconditions]] if @raw[:postconditions].is_a?(String)
100
- compile_conditions(@raw[:postconditions] ||= [], scope.postconditions)
103
+ compile_conditions(@raw[:postconditions] ||= [], config.postconditions)
104
+ end
105
+
106
+ def compile_errconditions
107
+ @raw[:errconditions] = [@raw[:errconditions]] if @raw[:errconditions].is_a?(String)
108
+ compile_conditions(@raw[:errconditions] ||= [], config.errconditions)
101
109
  end
102
110
 
103
111
  def compile_conditions(descriptions, conditions)
@@ -112,13 +120,13 @@ module Webspicy
112
120
  end
113
121
 
114
122
  def bind_examples
115
- (@raw[:examples] ||= []).each do |ex|
123
+ examples.each do |ex|
116
124
  ex.bind(self, false)
117
125
  end
118
126
  end
119
127
 
120
128
  def bind_counterexamples
121
- (@raw[:counterexamples] ||= []).each do |ex|
129
+ counterexamples.each do |ex|
122
130
  ex.bind(self, true)
123
131
  end
124
132
  end
@@ -1,17 +1,15 @@
1
1
  module Webspicy
2
2
  class Specification
3
3
  class TestCase
4
+ include Support::DataObject
4
5
 
5
6
  def initialize(raw)
6
- @raw = raw
7
+ super(raw)
7
8
  @counterexample = nil
8
9
  end
9
10
  attr_reader :service
10
11
  attr_reader :counterexample
11
12
 
12
- attr_accessor :raw
13
- protected :raw, :raw=
14
-
15
13
  def bind(service, counterexample)
16
14
  @service = service
17
15
  @counterexample = counterexample
@@ -111,13 +109,9 @@ module Webspicy
111
109
  !assert.empty?
112
110
  end
113
111
 
114
- def to_info
115
- @raw
116
- end
117
-
118
112
  def instrument(client)
119
113
  service.preconditions.each do |pre|
120
- pre.instrument(self, client)
114
+ pre.instrument(self, client) if pre.respond_to?(:instrument)
121
115
  end
122
116
  service.postconditions.each do |post|
123
117
  post.instrument(self, client) if post.respond_to?(:instrument)
@@ -1,2 +1,3 @@
1
+ require_relative 'support/data_object'
1
2
  require_relative 'support/status_range'
2
3
  require_relative 'support/colorize'
@@ -0,0 +1,25 @@
1
+ module Webspicy
2
+ module Support
3
+ module DataObject
4
+
5
+ def initialize(raw)
6
+ @raw = raw
7
+ end
8
+ attr_accessor :raw
9
+ protected :raw, :raw=
10
+
11
+ def method_missing(name, *args, &bl)
12
+ if @raw.has_key?(name) && args.empty? && bl.nil?
13
+ @raw[name]
14
+ else
15
+ super
16
+ end
17
+ end
18
+
19
+ def to_info
20
+ @raw
21
+ end
22
+
23
+ end # module DataObject
24
+ end # module Support
25
+ end # module Webspicy
@@ -1,89 +1,15 @@
1
1
  module Webspicy
2
2
  class Tester
3
- include Webspicy::Support::Colorize
4
3
 
5
- def initialize(config)
6
- @config = Configuration.dress(config)
7
- @test_suite = load
8
- end
9
- attr_reader :config
10
-
11
- def call(err=$stderr, out=$stdout)
12
- $_rspec_core_load_started_at = nil
13
- options = RSpec::Core::ConfigurationOptions.new(config.rspec_options)
14
- conf = RSpec::Core::Configuration.new
15
- RSpec::Core::Runner.new(options, conf).run(err, out)
16
- end
17
-
18
- # protected
19
-
20
- def load
21
- tester = self
22
- RSpec.reset
23
- rspec_config!
24
- RSpec.describe "" do
25
- before(:all) do
26
- tester.config.listeners(:before_all).each do |l|
27
- l.call(tester.config)
28
- end
29
- end
30
- after(:all) do
31
- tester.config.listeners(:after_all).each do |l|
32
- l.call(tester.config)
33
- end
34
- end
35
- tester.config.each_scope do |scope|
36
- client = scope.get_client
37
- scope.each_specification do |specification|
38
- scope.each_service(specification) do |service|
39
- tester.rspec_service!(self, service, client, scope)
40
- end
41
- end
42
- end
43
- end
44
- self
45
- end
46
-
47
- def rspec_service!(on, service, client, scope)
48
- scope.each_testcase(service) do |test_case|
49
- str = "#{service} #{test_case}"
50
- str = colorize_highlight(str)
51
- on.describe(str) do
52
- include_examples 'a successful test case invocation', client, test_case
53
- end
54
- end
55
- end
56
-
57
- def rspec_config!
58
- RSpec.shared_examples "a successful test case invocation" do |client, test_case|
59
-
60
- around(:each) do |example|
61
- client.around(test_case) do
62
- client.before(test_case)
63
- test_case.instrument(client)
64
- client.instrument(test_case)
65
- @response = client.call(test_case)
66
- @invocation = Tester::Invocation.new(test_case, @response, client)
67
- example.run
68
- client.after(test_case, @invocation)
69
- @invocation
70
- end
71
- end
72
-
73
- let(:invocation) do
74
- @invocation
75
- end
76
-
77
- it "meets its specification" do
78
- raise "Test not ran" unless invocation.done?
79
- invocation.rspec_assert!(self)
80
- end
81
- end
4
+ def self.new(*args, &bl)
5
+ require_relative 'rspec/tester'
6
+ RSpecTester.new(*args, &bl)
82
7
  end
83
8
 
84
9
  end # class Tester
85
10
  end # module Webspicy
86
11
  require_relative 'tester/client'
87
12
  require_relative 'tester/invocation'
13
+ require_relative 'tester/failure'
88
14
  require_relative 'tester/assertions'
89
15
  require_relative 'tester/asserter'