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.
- checksums.yaml +4 -4
- data/README.md +71 -24
- data/bin/webspicy +30 -14
- data/examples/restful/Gemfile +2 -2
- data/examples/restful/Gemfile.lock +53 -33
- data/examples/restful/Rakefile +0 -1
- data/examples/restful/app.rb +4 -1
- data/examples/restful/webspicy/config.rb +8 -0
- data/examples/restful/webspicy/rack.rb +1 -1
- data/examples/restful/webspicy/real.rb +1 -1
- data/examples/restful/webspicy/schema.fio +2 -2
- data/examples/restful/webspicy/support/must_be_authenticated.rb +2 -2
- data/examples/restful/webspicy/support/todo_removed.rb +18 -0
- data/examples/restful/webspicy/todo/deleteTodo.yml +4 -1
- data/examples/restful/webspicy/todo/getTodoSingleServiceFormat.yml +46 -0
- data/examples/restful/webspicy/todo/options.yml +1 -1
- data/examples/restful/webspicy/todo/patchTodo.yml +3 -0
- data/examples/restful/webspicy/todo/postFile.yml +1 -1
- data/examples/single_spec/spec.yml +59 -0
- data/examples/website/config.rb +2 -0
- data/examples/website/schema.fio +1 -0
- data/examples/website/specification/get-http.yml +30 -0
- data/examples/website/specification/get-https.yml +30 -0
- data/lib/finitio/webspicy/scalars.fio +25 -0
- data/lib/webspicy.rb +48 -17
- data/lib/webspicy/checker.rb +2 -2
- data/lib/webspicy/configuration.rb +70 -14
- data/lib/webspicy/configuration/scope.rb +162 -0
- data/lib/webspicy/configuration/single_url.rb +58 -0
- data/lib/webspicy/configuration/single_yml_file.rb +30 -0
- data/lib/webspicy/formaldoc.fio +23 -8
- data/lib/webspicy/mocker.rb +8 -8
- data/lib/webspicy/mocker/config.ru +5 -0
- data/lib/webspicy/openapi.rb +1 -0
- data/lib/webspicy/openapi/generator.rb +127 -0
- data/lib/webspicy/{resource.rb → specification.rb} +28 -5
- data/lib/webspicy/specification/file_upload.rb +37 -0
- data/lib/webspicy/specification/postcondition.rb +16 -0
- data/lib/webspicy/specification/precondition.rb +19 -0
- data/lib/webspicy/specification/precondition/global_request_headers.rb +35 -0
- data/lib/webspicy/specification/precondition/robust_to_invalid_input.rb +68 -0
- data/lib/webspicy/{resource → specification}/service.rb +11 -6
- data/lib/webspicy/specification/test_case.rb +139 -0
- data/lib/webspicy/support.rb +1 -0
- data/lib/webspicy/support/colorize.rb +28 -0
- data/lib/webspicy/support/status_range.rb +6 -1
- data/lib/webspicy/tester.rb +16 -11
- data/lib/webspicy/tester/asserter.rb +3 -2
- data/lib/webspicy/tester/assertions.rb +5 -1
- data/lib/webspicy/tester/client.rb +63 -0
- data/lib/webspicy/tester/client/http_client.rb +154 -0
- data/lib/webspicy/tester/client/rack_test_client.rb +188 -0
- data/lib/webspicy/tester/client/support.rb +65 -0
- data/lib/webspicy/tester/invocation.rb +218 -0
- data/lib/webspicy/tester/rspec_asserter.rb +108 -0
- data/lib/webspicy/tester/rspec_matchers.rb +104 -0
- data/lib/webspicy/version.rb +2 -2
- data/spec/{unit/spec_helper.rb → spec_helper.rb} +0 -0
- data/spec/unit/configuration/scope/test_each_service.rb +49 -0
- data/spec/unit/configuration/scope/test_each_specification.rb +68 -0
- data/spec/unit/configuration/scope/test_expand_example.rb +65 -0
- data/spec/unit/configuration/scope/test_to_real_url.rb +82 -0
- data/spec/unit/openapi/test_generator.rb +28 -0
- data/spec/unit/specification/precondition/test_global_request_headers.rb +42 -0
- data/spec/unit/{resource → specification}/service/test_dress_params.rb +2 -2
- data/spec/unit/specification/test_case/test_mutate.rb +24 -0
- data/spec/unit/{resource → specification}/test_instantiate_url.rb +5 -5
- data/spec/unit/{resource → specification}/test_url_placeholders.rb +4 -4
- data/spec/unit/test_configuration.rb +24 -7
- data/spec/unit/tester/client/test_around.rb +61 -0
- data/spec/unit/tester/test_asserter.rb +51 -0
- data/spec/unit/tester/test_assertions.rb +4 -4
- data/tasks/test.rake +3 -1
- metadata +88 -45
- data/LICENSE.md +0 -22
- data/lib/webspicy/client.rb +0 -61
- data/lib/webspicy/client/http_client.rb +0 -145
- data/lib/webspicy/client/rack_test_client.rb +0 -181
- data/lib/webspicy/client/support.rb +0 -48
- data/lib/webspicy/file_upload.rb +0 -35
- data/lib/webspicy/postcondition.rb +0 -14
- data/lib/webspicy/precondition.rb +0 -15
- data/lib/webspicy/resource/service/invocation.rb +0 -212
- data/lib/webspicy/resource/service/test_case.rb +0 -132
- data/lib/webspicy/scope.rb +0 -160
- data/spec/unit/client/test_around.rb +0 -59
- data/spec/unit/scope/test_each_resource.rb +0 -66
- data/spec/unit/scope/test_each_service.rb +0 -47
- data/spec/unit/scope/test_expand_example.rb +0 -63
- data/spec/unit/scope/test_to_real_url.rb +0 -80
@@ -1,5 +1,5 @@
|
|
1
1
|
module Webspicy
|
2
|
-
class
|
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.
|
91
|
+
s.specification = self
|
73
92
|
end
|
74
93
|
end
|
75
94
|
|
76
|
-
end
|
77
|
-
end
|
78
|
-
require_relative '
|
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
|
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 :
|
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} #{
|
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
|
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
|
data/lib/webspicy/support.rb
CHANGED
@@ -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
|