webspicy 0.15.7 → 0.16.3

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +71 -24
  3. data/bin/webspicy +30 -14
  4. data/examples/restful/Gemfile +2 -2
  5. data/examples/restful/Gemfile.lock +53 -33
  6. data/examples/restful/Rakefile +0 -1
  7. data/examples/restful/app.rb +4 -1
  8. data/examples/restful/webspicy/config.rb +8 -0
  9. data/examples/restful/webspicy/rack.rb +1 -1
  10. data/examples/restful/webspicy/real.rb +1 -1
  11. data/examples/restful/webspicy/schema.fio +2 -2
  12. data/examples/restful/webspicy/support/must_be_authenticated.rb +2 -2
  13. data/examples/restful/webspicy/support/todo_removed.rb +18 -0
  14. data/examples/restful/webspicy/todo/deleteTodo.yml +4 -1
  15. data/examples/restful/webspicy/todo/getTodoSingleServiceFormat.yml +46 -0
  16. data/examples/restful/webspicy/todo/options.yml +1 -1
  17. data/examples/restful/webspicy/todo/patchTodo.yml +3 -0
  18. data/examples/restful/webspicy/todo/postFile.yml +1 -1
  19. data/examples/single_spec/spec.yml +59 -0
  20. data/examples/website/config.rb +2 -0
  21. data/examples/website/schema.fio +1 -0
  22. data/examples/website/specification/get-http.yml +30 -0
  23. data/examples/website/specification/get-https.yml +30 -0
  24. data/lib/finitio/webspicy/scalars.fio +25 -0
  25. data/lib/webspicy.rb +48 -17
  26. data/lib/webspicy/checker.rb +2 -2
  27. data/lib/webspicy/configuration.rb +70 -14
  28. data/lib/webspicy/configuration/scope.rb +162 -0
  29. data/lib/webspicy/configuration/single_url.rb +58 -0
  30. data/lib/webspicy/configuration/single_yml_file.rb +30 -0
  31. data/lib/webspicy/formaldoc.fio +23 -8
  32. data/lib/webspicy/mocker.rb +8 -8
  33. data/lib/webspicy/mocker/config.ru +5 -0
  34. data/lib/webspicy/openapi.rb +1 -0
  35. data/lib/webspicy/openapi/generator.rb +127 -0
  36. data/lib/webspicy/{resource.rb → specification.rb} +28 -5
  37. data/lib/webspicy/specification/file_upload.rb +37 -0
  38. data/lib/webspicy/specification/postcondition.rb +16 -0
  39. data/lib/webspicy/specification/precondition.rb +19 -0
  40. data/lib/webspicy/specification/precondition/global_request_headers.rb +35 -0
  41. data/lib/webspicy/specification/precondition/robust_to_invalid_input.rb +68 -0
  42. data/lib/webspicy/{resource → specification}/service.rb +11 -6
  43. data/lib/webspicy/specification/test_case.rb +139 -0
  44. data/lib/webspicy/support.rb +1 -0
  45. data/lib/webspicy/support/colorize.rb +28 -0
  46. data/lib/webspicy/support/status_range.rb +6 -1
  47. data/lib/webspicy/tester.rb +16 -11
  48. data/lib/webspicy/tester/asserter.rb +3 -2
  49. data/lib/webspicy/tester/assertions.rb +5 -1
  50. data/lib/webspicy/tester/client.rb +63 -0
  51. data/lib/webspicy/tester/client/http_client.rb +154 -0
  52. data/lib/webspicy/tester/client/rack_test_client.rb +188 -0
  53. data/lib/webspicy/tester/client/support.rb +65 -0
  54. data/lib/webspicy/tester/invocation.rb +218 -0
  55. data/lib/webspicy/tester/rspec_asserter.rb +108 -0
  56. data/lib/webspicy/tester/rspec_matchers.rb +104 -0
  57. data/lib/webspicy/version.rb +2 -2
  58. data/spec/{unit/spec_helper.rb → spec_helper.rb} +0 -0
  59. data/spec/unit/configuration/scope/test_each_service.rb +49 -0
  60. data/spec/unit/configuration/scope/test_each_specification.rb +68 -0
  61. data/spec/unit/configuration/scope/test_expand_example.rb +65 -0
  62. data/spec/unit/configuration/scope/test_to_real_url.rb +82 -0
  63. data/spec/unit/openapi/test_generator.rb +28 -0
  64. data/spec/unit/specification/precondition/test_global_request_headers.rb +42 -0
  65. data/spec/unit/{resource → specification}/service/test_dress_params.rb +2 -2
  66. data/spec/unit/specification/test_case/test_mutate.rb +24 -0
  67. data/spec/unit/{resource → specification}/test_instantiate_url.rb +5 -5
  68. data/spec/unit/{resource → specification}/test_url_placeholders.rb +4 -4
  69. data/spec/unit/test_configuration.rb +24 -7
  70. data/spec/unit/tester/client/test_around.rb +61 -0
  71. data/spec/unit/tester/test_asserter.rb +51 -0
  72. data/spec/unit/tester/test_assertions.rb +4 -4
  73. data/tasks/test.rake +3 -1
  74. metadata +88 -45
  75. data/LICENSE.md +0 -22
  76. data/lib/webspicy/client.rb +0 -61
  77. data/lib/webspicy/client/http_client.rb +0 -145
  78. data/lib/webspicy/client/rack_test_client.rb +0 -181
  79. data/lib/webspicy/client/support.rb +0 -48
  80. data/lib/webspicy/file_upload.rb +0 -35
  81. data/lib/webspicy/postcondition.rb +0 -14
  82. data/lib/webspicy/precondition.rb +0 -15
  83. data/lib/webspicy/resource/service/invocation.rb +0 -212
  84. data/lib/webspicy/resource/service/test_case.rb +0 -132
  85. data/lib/webspicy/scope.rb +0 -160
  86. data/spec/unit/client/test_around.rb +0 -59
  87. data/spec/unit/scope/test_each_resource.rb +0 -66
  88. data/spec/unit/scope/test_each_service.rb +0 -47
  89. data/spec/unit/scope/test_expand_example.rb +0 -63
  90. data/spec/unit/scope/test_to_real_url.rb +0 -80
@@ -1,5 +1,5 @@
1
1
  module Webspicy
2
- class Resource
2
+ class Specification
3
3
 
4
4
  def initialize(raw, location = nil)
5
5
  @raw = raw
@@ -12,6 +12,17 @@ module Webspicy
12
12
  new(raw)
13
13
  end
14
14
 
15
+ def self.singleservice(raw)
16
+ converted = {
17
+ name: raw[:name] || "Unamed specification",
18
+ url: raw[:url],
19
+ services: [
20
+ Webspicy.service(raw.reject{|k| k==:url or k==:name }, Webspicy.current_scope)
21
+ ]
22
+ }
23
+ info(converted)
24
+ end
25
+
15
26
  def located_at!(location)
16
27
  @location = Path(location)
17
28
  end
@@ -22,6 +33,10 @@ module Webspicy
22
33
  file
23
34
  end
24
35
 
36
+ def name
37
+ @raw[:name]
38
+ end
39
+
25
40
  def url
26
41
  @raw[:url]
27
42
  end
@@ -51,6 +66,10 @@ module Webspicy
51
66
  @raw
52
67
  end
53
68
 
69
+ def to_singleservice
70
+ raise NotImplementedError
71
+ end
72
+
54
73
  private
55
74
 
56
75
  def extract_placeholder_value(params, placeholder, split = nil)
@@ -69,10 +88,14 @@ module Webspicy
69
88
 
70
89
  def bind_services
71
90
  (@raw[:services] ||= []).each do |s|
72
- s.resource = self
91
+ s.specification = self
73
92
  end
74
93
  end
75
94
 
76
- end
77
- end
78
- require_relative 'resource/service'
95
+ end # class Specification
96
+ end # module Webspicy
97
+ require_relative 'specification/service'
98
+ require_relative 'specification/precondition'
99
+ require_relative 'specification/postcondition'
100
+ require_relative 'specification/test_case'
101
+ require_relative 'specification/file_upload'
@@ -0,0 +1,37 @@
1
+ module Webspicy
2
+ class Specification
3
+ class FileUpload
4
+
5
+ def initialize(raw)
6
+ @path = raw[:path]
7
+ @content_type = raw[:content_type]
8
+ @param_name = raw[:param_name] || "file"
9
+ end
10
+
11
+ attr_reader :path, :content_type, :param_name
12
+
13
+ def self.info(raw)
14
+ new(raw)
15
+ end
16
+
17
+ def locate(specification)
18
+ FileUpload.new({
19
+ path: specification.locate(path),
20
+ content_type: content_type
21
+ })
22
+ end
23
+
24
+ def to_info
25
+ { path: path.to_s,
26
+ content_type: content_type,
27
+ param_name: param_name }
28
+ end
29
+
30
+ def to_s
31
+ "FileUpload(#{to_info})"
32
+ end
33
+ alias :inspect :to_s
34
+
35
+ end # class FileUpload
36
+ end # class Specification
37
+ end # module Webspicy
@@ -0,0 +1,16 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Postcondition
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 Postcondition
15
+ end # module Specification
16
+ end # module Webspicy
@@ -0,0 +1,19 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Precondition
4
+
5
+ def self.match(service, pre)
6
+ end
7
+
8
+ def instrument(test_case, client)
9
+ end
10
+
11
+ def counterexamples(service)
12
+ []
13
+ end
14
+
15
+ end # module Precondition
16
+ end # class Specification
17
+ end # module Webspicy
18
+ require_relative 'precondition/global_request_headers'
19
+ require_relative 'precondition/robust_to_invalid_input'
@@ -0,0 +1,35 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Precondition
4
+ class GlobalRequestHeaders
5
+ include Precondition
6
+
7
+ DEFAULT_OPTIONS = {}
8
+
9
+ def initialize(headers, options = {}, &bl)
10
+ @headers = headers
11
+ @options = DEFAULT_OPTIONS.merge(options)
12
+ @matcher = bl
13
+ end
14
+ attr_reader :headers, :matcher
15
+
16
+ def match(service, pre)
17
+ if matcher
18
+ return self if matcher.call(service)
19
+ nil
20
+ else
21
+ self
22
+ end
23
+ end
24
+
25
+ def instrument(test_case, client)
26
+ extra = headers.reject{|k|
27
+ test_case.headers.has_key?(k)
28
+ }
29
+ test_case.headers.merge!(extra)
30
+ end
31
+
32
+ end # class GlobalRequestHeaders
33
+ end # module Precondition
34
+ end # class Specification
35
+ end # module Webspicy
@@ -0,0 +1,68 @@
1
+ module Webspicy
2
+ class Specification
3
+ module Precondition
4
+ class RobustToInvalidInput
5
+ include Precondition
6
+
7
+ def self.match(service, pre)
8
+ self.new
9
+ end
10
+
11
+ def match(service, pre)
12
+ self
13
+ end
14
+
15
+ def counterexamples(service)
16
+ spec = service.specification
17
+ first = service.examples.first
18
+ cexamples = []
19
+ cexamples += url_randomness_counterexamples(service, first) if first
20
+ cexamples += empty_input_counterexamples(service, first) if first
21
+ cexamples
22
+ end
23
+
24
+ protected
25
+
26
+ def url_randomness_counterexamples(service, first)
27
+ service.specification.url_placeholders.map{|p|
28
+ first.mutate({
29
+ :description => "it is robust to URL randomness on param `#{p}` (RobustToInvalidInput)",
30
+ :dress_params => false,
31
+ :params => first.params.merge(p => (SecureRandom.random_number * 100000000).to_i),
32
+ :expected => {
33
+ status: Support::StatusRange.str("4xx")
34
+ },
35
+ :assert => []
36
+ })
37
+ }
38
+ end
39
+
40
+ def empty_input_counterexamples(service, first)
41
+ placeholders = service.specification.url_placeholders
42
+ empty_input = first.params.reject{|k| !placeholders.include?(k) }
43
+ if invalid_input?(service, empty_input)
44
+ [first.mutate({
45
+ :description => "it is robust to an invalid empty input (RobustToInvalidInput)",
46
+ :dress_params => false,
47
+ :params => empty_input,
48
+ :expected => {
49
+ status: Support::StatusRange.str("4xx")
50
+ },
51
+ :assert => []
52
+ })]
53
+ else
54
+ []
55
+ end
56
+ end
57
+
58
+ def invalid_input?(service, empty_input)
59
+ service.input_schema.dress(empty_input)
60
+ false
61
+ rescue Finitio::Error
62
+ true
63
+ end
64
+
65
+ end # class RobustToInvalidInput
66
+ end # module Precondition
67
+ end # class Specification
68
+ end # module Webspicy
@@ -1,5 +1,5 @@
1
1
  module Webspicy
2
- class Resource
2
+ class Specification
3
3
  class Service
4
4
 
5
5
  def initialize(raw)
@@ -9,7 +9,7 @@ module Webspicy
9
9
  @preconditions = compile_preconditions
10
10
  @postconditions = compile_postconditions
11
11
  end
12
- attr_accessor :resource
12
+ attr_accessor :specification
13
13
 
14
14
  def self.info(raw)
15
15
  new(raw)
@@ -19,6 +19,10 @@ module Webspicy
19
19
  @raw[:method]
20
20
  end
21
21
 
22
+ def description
23
+ @raw[:description]
24
+ end
25
+
22
26
  def preconditions
23
27
  @preconditions
24
28
  end
@@ -77,7 +81,7 @@ module Webspicy
77
81
  end
78
82
 
79
83
  def to_s
80
- "#{method} #{resource.url}"
84
+ "#{method} #{specification.url}"
81
85
  end
82
86
 
83
87
  private
@@ -97,6 +101,9 @@ module Webspicy
97
101
  end
98
102
 
99
103
  def compile_conditions(descriptions, conditions)
104
+ # Because we want pre & post to be able to match in all cases
105
+ # we need at least one condition
106
+ descriptions = ["all"] if descriptions.empty?
100
107
  descriptions
101
108
  .map{|descr|
102
109
  conditions.map{|c| c.match(self, descr) }.compact
@@ -117,7 +124,5 @@ module Webspicy
117
124
  end
118
125
 
119
126
  end # class Service
120
- end # class Resource
127
+ end # class Specification
121
128
  end # module Webspicy
122
- require_relative 'service/test_case'
123
- require_relative 'service/invocation'
@@ -0,0 +1,139 @@
1
+ module Webspicy
2
+ class Specification
3
+ class TestCase
4
+
5
+ def initialize(raw)
6
+ @raw = raw
7
+ @counterexample = nil
8
+ end
9
+ attr_reader :service
10
+ attr_reader :counterexample
11
+
12
+ attr_accessor :raw
13
+ protected :raw, :raw=
14
+
15
+ def bind(service, counterexample)
16
+ @service = service
17
+ @counterexample = counterexample
18
+ self
19
+ end
20
+
21
+ def counterexample?
22
+ !!@counterexample
23
+ end
24
+
25
+ def specification
26
+ service.specification
27
+ end
28
+
29
+ def self.info(raw)
30
+ new(raw)
31
+ end
32
+
33
+ def description
34
+ @raw[:description]
35
+ end
36
+
37
+ def seeds
38
+ @raw[:seeds]
39
+ end
40
+
41
+ def headers
42
+ @raw[:headers] ||= {}
43
+ end
44
+
45
+ def metadata
46
+ @raw[:metadata] ||= {}
47
+ end
48
+
49
+ def tags
50
+ @raw[:tags] ||= []
51
+ end
52
+
53
+ def dress_params
54
+ @raw.fetch(:dress_params){ true }
55
+ end
56
+ alias :dress_params? :dress_params
57
+
58
+ def params
59
+ @raw[:params] || {}
60
+ end
61
+
62
+ def body
63
+ @raw[:body]
64
+ end
65
+
66
+ def file_upload
67
+ @raw[:file_upload]
68
+ end
69
+
70
+ def located_file_upload
71
+ file_upload ? file_upload.locate(specification) : nil
72
+ end
73
+
74
+ def expected
75
+ @raw[:expected] || {}
76
+ end
77
+
78
+ def expected_content_type
79
+ expected[:content_type]
80
+ end
81
+
82
+ def expected_status
83
+ expected[:status]
84
+ end
85
+
86
+ def is_expected_status?(status)
87
+ expected_status === status
88
+ end
89
+
90
+ def expected_error
91
+ expected[:error]
92
+ end
93
+
94
+ def has_expected_error?
95
+ !expected_error.nil?
96
+ end
97
+
98
+ def expected_headers
99
+ expected[:headers] || {}
100
+ end
101
+
102
+ def has_expected_headers?
103
+ !expected_headers.empty?
104
+ end
105
+
106
+ def assert
107
+ @raw[:assert] || []
108
+ end
109
+
110
+ def has_assertions?
111
+ !assert.empty?
112
+ end
113
+
114
+ def to_info
115
+ @raw
116
+ end
117
+
118
+ def instrument(client)
119
+ service.preconditions.each do |pre|
120
+ pre.instrument(self, client)
121
+ end
122
+ service.postconditions.each do |post|
123
+ post.instrument(self, client) if post.respond_to?(:instrument)
124
+ end
125
+ end
126
+
127
+ def mutate(override)
128
+ m = self.dup
129
+ m.raw = self.raw.merge(override)
130
+ m
131
+ end
132
+
133
+ def to_s
134
+ description
135
+ end
136
+
137
+ end # class TestCase
138
+ end # class Specification
139
+ end # module Webspicy
@@ -1 +1,2 @@
1
1
  require_relative 'support/status_range'
2
+ require_relative 'support/colorize'
@@ -0,0 +1,28 @@
1
+ module Webspicy
2
+ module Support
3
+ module Colorize
4
+
5
+ def colorize(str, kind, config = nil)
6
+ color = (config || self.config).colors[kind]
7
+ ColorizedString[str].colorize(color)
8
+ end
9
+ module_function :colorize
10
+
11
+ def colorize_highlight(str, cfg = nil)
12
+ colorize(str, :highlight, cfg)
13
+ end
14
+ module_function :colorize_highlight
15
+
16
+ def colorize_success(str, cfg = nil)
17
+ colorize(str, :success, cfg)
18
+ end
19
+ module_function :colorize_success
20
+
21
+ def colorize_error(str, cfg = nil)
22
+ colorize(str, :error, cfg)
23
+ end
24
+ module_function :colorize_error
25
+
26
+ end # module Colorize
27
+ end # module Support
28
+ end # module Webspicy