webspicy 0.19.0 → 0.20.4

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/bin/webspicy +1 -2
  3. data/lib/webspicy.rb +0 -2
  4. data/lib/webspicy/configuration.rb +15 -0
  5. data/lib/webspicy/configuration/scope.rb +1 -0
  6. data/lib/webspicy/specification.rb +4 -3
  7. data/lib/webspicy/specification/condition.rb +29 -4
  8. data/lib/webspicy/specification/err.rb +18 -0
  9. data/lib/webspicy/specification/oldies.rb +4 -0
  10. data/lib/webspicy/specification/oldies/bridge.rb +32 -0
  11. data/lib/webspicy/specification/{errcondition.rb → oldies/errcondition.rb} +5 -0
  12. data/lib/webspicy/specification/{postcondition.rb → oldies/postcondition.rb} +5 -0
  13. data/lib/webspicy/specification/{precondition.rb → oldies/precondition.rb} +5 -2
  14. data/lib/webspicy/specification/post.rb +20 -0
  15. data/lib/webspicy/specification/post/missing_condition_impl.rb +15 -0
  16. data/lib/webspicy/specification/post/unexpected_condition_impl.rb +15 -0
  17. data/lib/webspicy/specification/pre.rb +19 -0
  18. data/lib/webspicy/specification/{precondition → pre}/global_request_headers.rb +4 -4
  19. data/lib/webspicy/specification/{precondition → pre}/robust_to_invalid_input.rb +4 -4
  20. data/lib/webspicy/specification/service.rb +29 -5
  21. data/lib/webspicy/specification/test_case.rb +3 -0
  22. data/lib/webspicy/support.rb +12 -2
  23. data/lib/webspicy/support/colorize.rb +6 -0
  24. data/lib/webspicy/tester.rb +89 -27
  25. data/lib/webspicy/tester/assertions.rb +2 -2
  26. data/lib/webspicy/tester/client.rb +0 -26
  27. data/lib/webspicy/tester/fakeses.rb +41 -0
  28. data/lib/webspicy/tester/fakeses/email.rb +38 -0
  29. data/lib/webspicy/tester/fakesmtp.rb +39 -0
  30. data/lib/webspicy/tester/fakesmtp/email.rb +27 -0
  31. data/lib/webspicy/tester/reporter.rb +5 -0
  32. data/lib/webspicy/tester/reporter/documentation.rb +31 -8
  33. data/lib/webspicy/tester/reporter/error_count.rb +11 -7
  34. data/lib/webspicy/tester/reporter/exceptions.rb +2 -0
  35. data/lib/webspicy/tester/reporter/file_progress.rb +5 -2
  36. data/lib/webspicy/tester/reporter/file_summary.rb +3 -2
  37. data/lib/webspicy/tester/reporter/progress.rb +6 -4
  38. data/lib/webspicy/tester/reporter/summary.rb +9 -7
  39. data/lib/webspicy/tester/result.rb +16 -13
  40. data/lib/webspicy/tester/result/errcondition_met.rb +1 -3
  41. data/lib/webspicy/tester/result/error_schema_met.rb +1 -0
  42. data/lib/webspicy/tester/result/invocation_succeeded.rb +13 -0
  43. data/lib/webspicy/tester/result/output_schema_met.rb +1 -0
  44. data/lib/webspicy/tester/result/postcondition_met.rb +1 -3
  45. data/lib/webspicy/version.rb +2 -2
  46. data/lib/webspicy/web/invocation.rb +7 -3
  47. data/spec/blackbox/commandline.yml +24 -0
  48. data/spec/blackbox/fixtures/passing/config.rb +9 -0
  49. data/spec/blackbox/fixtures/passing/formaldef/get.yml +30 -0
  50. data/spec/unit/specification/{precondition → pre}/test_global_request_headers.rb +9 -4
  51. data/spec/unit/specification/test_condition.rb +18 -0
  52. data/spec/unit/tester/fakeses/test_email.rb +40 -0
  53. data/tasks/test.rake +2 -1
  54. metadata +34 -18
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 614e5904931375bd6f4df435d33736cde757e752d0d759fbc3af4e185b9cd9e4
4
- data.tar.gz: 8d63e07e93809bd5139300e5206f0a3b3204e86d77faea60a29302e5787413d1
3
+ metadata.gz: 9afe697a47ba4391d2e059f1bf3707c04f97f9d6fd50557104a59c6602dbd75c
4
+ data.tar.gz: 66ec4036f8e76dad7af0f8b0cf83ea139a67a7b1020aa0a304c26fdc9402d593
5
5
  SHA512:
6
- metadata.gz: 33d6a69e097e14a90f8b69edb4833a4a1325cec38513bf8f88d017b31847e08b310acce50dc7fb3d3cbdeefdff76b3497b489adbc2e965c7e8b12f0228a63524
7
- data.tar.gz: 0d6cf55d54a6bb94b48511f9c178a1e5e673772749784b3c7c720d9413670dc1e93029cfa0687efd8fced6f91eb699ea5cae4ab15e40a674dea7843712dec114
6
+ metadata.gz: 79feb96443a47523b38f16e4cf7e7edfcd675f5a71c6b33aa452ad452aeacd5e64de1ce1ffa9ff3f5bf9cf428d59dc183e8f1455e1ce26126f2eddf9a14795fb
7
+ data.tar.gz: 2761eb20d67f4c697dedf7c27496cfd36ceed4f988f1f54f17809358e570102f1e40c29c47e46f614671504dcf1c877ce2fe6ccb7a72b510994a9da95bb74de1
data/bin/webspicy CHANGED
@@ -55,5 +55,4 @@ if ARGV.size != 1
55
55
  end
56
56
 
57
57
  config = Webspicy::Configuration.dress(ARGV[0])
58
- res = Webspicy::Tester.new(config).call
59
- abort("#{res} errors occured") unless res == 0
58
+ Webspicy::Tester.new(config).call!
data/lib/webspicy.rb CHANGED
@@ -33,8 +33,6 @@ module Webspicy
33
33
  HttpClient = Web::HttpClient
34
34
  RackTestClient = Web::RackTestClient
35
35
  Resource = Specification
36
- Precondition = Specification::Precondition
37
- Postcondition = Specification::Postcondition
38
36
  FileUpload = Specification::FileUpload
39
37
  Scope = Configuration::Scope
40
38
  Checker = Tester::FileChecker
@@ -33,12 +33,15 @@ module Webspicy
33
33
  @service_filter = default_service_filter
34
34
  @test_case_filter = default_test_case_filter
35
35
  @colors = {
36
+ :section => :magenta,
36
37
  :highlight => :cyan,
37
38
  :error => :red,
38
39
  :success => :green
39
40
  }
41
+ @colorize = true
40
42
  @scope_factory = ->(config){ Scope.new(config) }
41
43
  @client = Web::HttpClient
44
+ @reporter = default_reporter
42
45
  Path.require_tree(@folder/'support') if (@folder/'support').exists?
43
46
  @world = Support::World.new(folder/'world', self)
44
47
  yield(self) if block_given?
@@ -47,6 +50,8 @@ module Webspicy
47
50
  protected :folder=
48
51
 
49
52
  attr_accessor :colors
53
+ attr_accessor :colorize
54
+
50
55
  attr_reader :world
51
56
 
52
57
  def self.dress(arg, &bl)
@@ -434,6 +439,16 @@ module Webspicy
434
439
  end
435
440
  private :default_rspec_options
436
441
 
442
+ # Returns the default reporter to use.
443
+ def default_reporter
444
+ @reporter = Tester::Reporter::Composite.new
445
+ @reporter << Tester::Reporter::Documentation.new
446
+ @reporter << Tester::Reporter::Exceptions.new
447
+ @reporter << Tester::Reporter::Summary.new
448
+ @reporter << Tester::Reporter::ErrorCount.new
449
+ end
450
+ attr_accessor :reporter
451
+
437
452
  # Returns the Data system to use for parsing schemas
438
453
  #
439
454
  # The data system associated with a configuration is build when the
@@ -23,6 +23,7 @@ module Webspicy
23
23
  folder = config.folder
24
24
  world = config.folder/"world"
25
25
  fs = folder.glob("**/*.yml").reject{|f| f.to_s.start_with?(world.to_s) }
26
+ fs = fs.sort
26
27
  fs = fs.select(&to_filter_proc(config.file_filter)) if apply_filter
27
28
  fs.each do |file|
28
29
  yield file, folder
@@ -94,8 +94,9 @@ module Webspicy
94
94
  end # module Webspicy
95
95
  require_relative 'specification/service'
96
96
  require_relative 'specification/condition'
97
- require_relative 'specification/precondition'
98
- require_relative 'specification/postcondition'
99
- require_relative 'specification/errcondition'
97
+ require_relative 'specification/pre'
98
+ require_relative 'specification/post'
99
+ require_relative 'specification/err'
100
+ require_relative 'specification/oldies'
100
101
  require_relative 'specification/test_case'
101
102
  require_relative 'specification/file_upload'
@@ -1,11 +1,40 @@
1
1
  module Webspicy
2
2
  class Specification
3
3
  module Condition
4
+ extend Forwardable
4
5
 
5
6
  MATCH_ALL = "__all__"
6
7
 
7
8
  attr_accessor :matching_description
8
9
 
10
+ # Given a service and a condition, returns a Pre instance of there is a
11
+ # match, nil otherwise.
12
+ def self.match(service, condition)
13
+ end
14
+
15
+ # Bind the condition instance to a current tester.
16
+ def bind(tester)
17
+ @tester = tester
18
+ self
19
+ end
20
+ attr_reader :tester
21
+
22
+ def_delegators :@tester, *[
23
+ :config, :scope, :client,
24
+ :specification, :spec_file,
25
+ :service, :test_case,
26
+ :invocation, :result,
27
+ :reporter
28
+ ]
29
+
30
+ def sooner_or_later(*args, &bl)
31
+ Webspicy::Support.sooner_or_later(*args, &bl)
32
+ end
33
+
34
+ def fail!(msg)
35
+ raise Tester::Failure, msg
36
+ end
37
+
9
38
  def to_s
10
39
  if matching_description == MATCH_ALL
11
40
  self.class.name
@@ -14,10 +43,6 @@ module Webspicy
14
43
  end
15
44
  end
16
45
 
17
- def sooner_or_later(*args, &bl)
18
- Webspicy::Support.sooner_or_later(*args, &bl)
19
- end
20
-
21
46
  end # module Condition
22
47
  end # class Specification
23
48
  end # module Webspicy
@@ -0,0 +1,18 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Err
4
+ include Condition
5
+
6
+ # Instrument the current test_case so as to prepare for errcondition
7
+ # check later
8
+ def instrument
9
+ end
10
+
11
+ # Check that the errcondition is met on last invocation & result
12
+ # of an counterexample
13
+ def check!
14
+ end
15
+
16
+ end # module Err
17
+ end # module Specification
18
+ end # module Webspicy
@@ -0,0 +1,4 @@
1
+ require_relative 'oldies/precondition'
2
+ require_relative 'oldies/postcondition'
3
+ require_relative 'oldies/errcondition'
4
+ require_relative 'oldies/bridge'
@@ -0,0 +1,32 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Oldies
4
+ class Bridge
5
+ include Condition
6
+
7
+ def initialize(target)
8
+ @target = target
9
+ end
10
+ attr_reader :target
11
+
12
+ def instrument
13
+ return unless target.respond_to?(:instrument)
14
+ target.instrument(test_case, client)
15
+ end
16
+
17
+ def check!
18
+ return unless target.respond_to?(:check)
19
+ res = target.check(invocation)
20
+ res ? fail!(res) : nil
21
+ end
22
+
23
+ def to_s
24
+ "#{target} (backward compatibility bridge)"
25
+ end
26
+
27
+ end # class Bridge
28
+ end # module Errcondition
29
+ end # module Specification
30
+ Precondition = Specification::Precondition
31
+ Postcondition = Specification::Postcondition
32
+ end # module Webspicy
@@ -1,8 +1,13 @@
1
1
  module Webspicy
2
2
  class Specification
3
+ # Deprecated, use Err instead
3
4
  module Errcondition
4
5
  include Condition
5
6
 
7
+ def bind(tester)
8
+ Oldies::Bridge.new(self).bind(tester)
9
+ end
10
+
6
11
  def self.match(service, descr)
7
12
  end
8
13
 
@@ -1,8 +1,13 @@
1
1
  module Webspicy
2
2
  class Specification
3
+ # Deprecated, use Post instead
3
4
  module Postcondition
4
5
  include Condition
5
6
 
7
+ def bind(tester)
8
+ Oldies::Bridge.new(self).bind(tester)
9
+ end
10
+
6
11
  def self.match(service, descr)
7
12
  end
8
13
 
@@ -1,8 +1,13 @@
1
1
  module Webspicy
2
2
  class Specification
3
+ # Deprecated, use Pre instead
3
4
  module Precondition
4
5
  include Condition
5
6
 
7
+ def bind(tester)
8
+ Oldies::Bridge.new(self).bind(tester)
9
+ end
10
+
6
11
  def self.match(service, pre)
7
12
  end
8
13
 
@@ -16,5 +21,3 @@ module Webspicy
16
21
  end # module Precondition
17
22
  end # class Specification
18
23
  end # module Webspicy
19
- require_relative 'precondition/global_request_headers'
20
- require_relative 'precondition/robust_to_invalid_input'
@@ -0,0 +1,20 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Post
4
+ include Condition
5
+
6
+ # Instrument the current test_case so as to prepare for postcondition
7
+ # check later
8
+ def instrument
9
+ end
10
+
11
+ # Check that the postcondition is met on last invocation & result
12
+ # of an example
13
+ def check!
14
+ end
15
+
16
+ end # module Post
17
+ end # module Specification
18
+ end # module Webspicy
19
+ require_relative "post/missing_condition_impl"
20
+ require_relative "post/unexpected_condition_impl"
@@ -0,0 +1,15 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Postcondition
4
+ class MissingConditionImpl
5
+ include Post
6
+
7
+ def check!
8
+ msg = matching_description.gsub(/\(x\)/, "<!>")
9
+ fail!("#{msg} (not instrumented)")
10
+ end
11
+
12
+ end # class MissingConditionImpl
13
+ end # module Postcondition
14
+ end # class Specification
15
+ end # module Webspicy
@@ -0,0 +1,15 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Postcondition
4
+ class UnexpectedConditionImpl
5
+ include Post
6
+
7
+ def check!
8
+ msg = matching_description.gsub(/\( \)/, "<x>")
9
+ fail!("#{msg} (is instrumented)")
10
+ end
11
+
12
+ end # class UnexpectedConditionImpl
13
+ end # module Postcondition
14
+ end # class Specification
15
+ end # module Webspicy
@@ -0,0 +1,19 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Pre
4
+ include Condition
5
+
6
+ # Instrument the current test_case so as to meet the precondition
7
+ def instrument
8
+ end
9
+
10
+ # Provide counterexamples of this precondition for a given service.
11
+ def counterexamples(service)
12
+ []
13
+ end
14
+
15
+ end # module Pre
16
+ end # class Specification
17
+ end # module Webspicy
18
+ require_relative 'pre/global_request_headers'
19
+ require_relative 'pre/robust_to_invalid_input'
@@ -1,8 +1,8 @@
1
1
  module Webspicy
2
2
  class Specification
3
- module Precondition
3
+ module Pre
4
4
  class GlobalRequestHeaders
5
- include Precondition
5
+ include Pre
6
6
 
7
7
  DEFAULT_OPTIONS = {}
8
8
 
@@ -22,7 +22,7 @@ module Webspicy
22
22
  end
23
23
  end
24
24
 
25
- def instrument(test_case, client)
25
+ def instrument
26
26
  extra = headers.reject{|k|
27
27
  test_case.headers.has_key?(k)
28
28
  }
@@ -30,6 +30,6 @@ module Webspicy
30
30
  end
31
31
 
32
32
  end # class GlobalRequestHeaders
33
- end # module Precondition
33
+ end # module Pre
34
34
  end # class Specification
35
35
  end # module Webspicy
@@ -1,8 +1,8 @@
1
1
  module Webspicy
2
2
  class Specification
3
- module Precondition
3
+ module Pre
4
4
  class RobustToInvalidInput
5
- include Precondition
5
+ include Pre
6
6
 
7
7
  def self.match(service, pre)
8
8
  self.new
@@ -49,7 +49,7 @@ module Webspicy
49
49
  status: Support::StatusRange.str("4xx")
50
50
  },
51
51
  :assert => []
52
- })]
52
+ })]
53
53
  else
54
54
  []
55
55
  end
@@ -63,6 +63,6 @@ module Webspicy
63
63
  end
64
64
 
65
65
  end # class RobustToInvalidInput
66
- end # module Precondition
66
+ end # module Pre
67
67
  end # class Specification
68
68
  end # module Webspicy
@@ -10,6 +10,9 @@ module Webspicy
10
10
  end
11
11
  attr_accessor :specification
12
12
 
13
+ # Deprecated
14
+ alias :resource :specification
15
+
13
16
  def self.info(raw)
14
17
  new(raw)
15
18
  end
@@ -112,16 +115,37 @@ module Webspicy
112
115
  # Because we want pre & post to be able to match in all cases
113
116
  # we need at least one condition
114
117
  descriptions = [Condition::MATCH_ALL] if descriptions.empty?
115
- conditions.map{|c|
118
+ mapping = {}
119
+ instances = conditions.map{|c|
116
120
  instance = nil
117
121
  descr = descriptions.find do |d|
118
122
  instance = c.match(self, d)
119
123
  end
120
- if instance && instance.respond_to?(:matching_description=)
121
- instance.matching_description = descr
122
- end
123
- instance
124
+ instance.tap{|i|
125
+ mapping[descr] ||= i if i
126
+ i.matching_description = descr if i.respond_to?(:matching_description=)
127
+ }
124
128
  }.compact
129
+ mapped = descriptions
130
+ .select{|d| mapping[d] }
131
+ .map{|d| mapping[d] }
132
+ unmapped = descriptions
133
+ .reject{|d| mapping[d] }
134
+ .select{|d| d.strip =~ /^(\(\w+\))?\(x\)/ }
135
+ .map{|d|
136
+ Postcondition::MissingConditionImpl.new.tap{|mc|
137
+ mc.matching_description = d
138
+ }
139
+ }
140
+ unexpected = descriptions
141
+ .select{|d| mapping[d] }
142
+ .select{|d| d.strip =~ /^(\(\w+\))?\( \)/ }
143
+ .map{|d|
144
+ Postcondition::UnexpectedConditionImpl.new.tap{|mc|
145
+ mc.matching_description = d
146
+ }
147
+ }
148
+ mapped + unmapped + unexpected
125
149
  end
126
150
 
127
151
  def bind_examples