webspicy 0.20.0 → 0.20.5
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/bin/sandr +14 -0
- data/bin/search +2 -0
- data/bin/webspicy +3 -4
- data/lib/finitio/webspicy/shared.fio +10 -0
- data/lib/webspicy.rb +22 -53
- data/lib/webspicy/configuration.rb +17 -0
- data/lib/webspicy/configuration/scope.rb +3 -2
- data/lib/webspicy/configuration/single_url.rb +35 -25
- data/lib/webspicy/configuration/single_yml_file.rb +7 -2
- data/lib/webspicy/specification.rb +0 -55
- data/lib/webspicy/specification/oldies/bridge.rb +4 -1
- data/lib/webspicy/specification/post/missing_condition_impl.rb +2 -2
- data/lib/webspicy/specification/post/unexpected_condition_impl.rb +2 -2
- data/lib/webspicy/specification/service.rb +4 -5
- data/lib/webspicy/specification/test_case.rb +3 -49
- data/lib/webspicy/support/colorize.rb +7 -1
- data/lib/webspicy/tester.rb +21 -20
- data/lib/webspicy/tester/assertions.rb +2 -2
- data/lib/webspicy/tester/reporter.rb +5 -0
- data/lib/webspicy/tester/reporter/documentation.rb +26 -8
- data/lib/webspicy/tester/reporter/error_count.rb +11 -7
- data/lib/webspicy/tester/reporter/file_progress.rb +2 -2
- data/lib/webspicy/tester/reporter/file_summary.rb +2 -2
- data/lib/webspicy/tester/reporter/progress.rb +4 -4
- data/lib/webspicy/tester/reporter/summary.rb +8 -7
- data/lib/webspicy/tester/result.rb +2 -2
- data/lib/webspicy/tester/result/errcondition_met.rb +1 -3
- data/lib/webspicy/tester/result/error_schema_met.rb +1 -0
- data/lib/webspicy/tester/result/output_schema_met.rb +1 -0
- data/lib/webspicy/tester/result/postcondition_met.rb +1 -3
- data/lib/webspicy/version.rb +1 -1
- data/lib/webspicy/web.rb +46 -0
- data/lib/webspicy/{formaldoc.fio → web/formaldoc.fio} +5 -13
- data/lib/webspicy/web/invocation.rb +7 -3
- data/lib/webspicy/web/specification.rb +68 -0
- data/lib/webspicy/web/specification/file_upload.rb +39 -0
- data/lib/webspicy/web/specification/service.rb +13 -0
- data/lib/webspicy/web/specification/test_case.rb +58 -0
- data/spec/unit/configuration/scope/test_expand_example.rb +11 -5
- data/spec/unit/specification/pre/test_global_request_headers.rb +3 -3
- data/spec/unit/specification/service/test_dress_params.rb +2 -2
- data/spec/unit/test_configuration.rb +1 -0
- data/spec/unit/web/specification/test_instantiate_url.rb +36 -0
- data/spec/unit/web/specification/test_url_placeholders.rb +23 -0
- data/tasks/test.rake +2 -1
- metadata +24 -16
- data/lib/webspicy/specification/file_upload.rb +0 -37
- data/spec/unit/specification/test_instantiate_url.rb +0 -34
- data/spec/unit/specification/test_url_placeholders.rb +0 -21
@@ -96,13 +96,13 @@ module Webspicy
|
|
96
96
|
|
97
97
|
def check_postconditions!
|
98
98
|
service.postconditions.each do |c|
|
99
|
-
check_one! Result::PostconditionMet.new(self,
|
99
|
+
check_one! Result::PostconditionMet.new(self, tester.bind_condition(c))
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
103
|
def check_errconditions!
|
104
104
|
service.errconditions.each do |c|
|
105
|
-
check_one! Result::ErrconditionMet.new(self,
|
105
|
+
check_one! Result::ErrconditionMet.new(self, tester.bind_condition(c))
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
data/lib/webspicy/version.rb
CHANGED
data/lib/webspicy/web.rb
CHANGED
@@ -1,3 +1,49 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Web
|
3
|
+
|
4
|
+
require_relative 'web/specification'
|
5
|
+
|
6
|
+
FORMALDOC = Finitio.system(Path.dir/("web/formaldoc.fio"))
|
7
|
+
|
8
|
+
def specification(raw, file = nil, scope = Webspicy.default_scope)
|
9
|
+
raw = YAML.load(raw) if raw.is_a?(String)
|
10
|
+
Webspicy.with_scope(scope) do
|
11
|
+
r = FORMALDOC["Specification"].dress(raw)
|
12
|
+
r.config = scope.config
|
13
|
+
r.located_at!(file) if file
|
14
|
+
r
|
15
|
+
end
|
16
|
+
rescue Finitio::Error => ex
|
17
|
+
handle_finitio_error(ex)
|
18
|
+
end
|
19
|
+
module_function :specification
|
20
|
+
|
21
|
+
def service(raw, scope = Webspicy.default_scope)
|
22
|
+
Webspicy.with_scope(scope) do
|
23
|
+
FORMALDOC["Service"].dress(raw)
|
24
|
+
end
|
25
|
+
rescue Finitio::Error => ex
|
26
|
+
handle_finitio_error(ex)
|
27
|
+
end
|
28
|
+
module_function :service
|
29
|
+
|
30
|
+
def test_case(raw, scope = Webspicy.default_scope)
|
31
|
+
Webspicy.with_scope(scope) do
|
32
|
+
FORMALDOC["TestCase"].dress(raw)
|
33
|
+
end
|
34
|
+
rescue Finitio::Error => ex
|
35
|
+
handle_finitio_error(ex)
|
36
|
+
end
|
37
|
+
module_function :test_case
|
38
|
+
|
39
|
+
def handle_finitio_error(ex)
|
40
|
+
puts ex.root_cause.message
|
41
|
+
raise ex
|
42
|
+
end
|
43
|
+
module_function :handle_finitio_error
|
44
|
+
|
45
|
+
end # module Web
|
46
|
+
end # module Webspicy
|
1
47
|
require_relative 'web/client'
|
2
48
|
require_relative 'web/invocation'
|
3
49
|
require_relative 'web/mocker'
|
@@ -1,23 +1,17 @@
|
|
1
1
|
@import finitio/data
|
2
|
+
@import webspicy/shared
|
2
3
|
|
3
4
|
Method =
|
4
5
|
String( s | s =~ /^(GET|POST|POST_FORM|PUT|DELETE|PATCH|PUT|OPTIONS)$/ )
|
5
6
|
|
6
|
-
Tag = String( s | s.length > 0 )
|
7
|
-
|
8
|
-
Schema =
|
9
|
-
.Finitio::System <fio> String
|
10
|
-
\( s | ::Webspicy.schema(s) )
|
11
|
-
\( s | raise "Unsupported" )
|
12
|
-
|
13
7
|
FileUpload =
|
14
|
-
.Webspicy::FileUpload <info> {
|
8
|
+
.Webspicy::Web::Specification::FileUpload <info> {
|
15
9
|
path : String
|
16
10
|
content_type : String
|
17
11
|
param_name :? String
|
18
12
|
}
|
19
13
|
|
20
|
-
Specification = .Webspicy::Specification
|
14
|
+
Specification = .Webspicy::Web::Specification
|
21
15
|
<info> {
|
22
16
|
name: String
|
23
17
|
url: String
|
@@ -41,7 +35,7 @@ Specification = .Webspicy::Specification
|
|
41
35
|
}
|
42
36
|
|
43
37
|
Service =
|
44
|
-
.Webspicy::Specification::Service <info> {
|
38
|
+
.Webspicy::Web::Specification::Service <info> {
|
45
39
|
method : Method
|
46
40
|
description : String
|
47
41
|
preconditions :? [String]|String
|
@@ -57,7 +51,7 @@ Service =
|
|
57
51
|
}
|
58
52
|
|
59
53
|
TestCase =
|
60
|
-
.Webspicy::Specification::TestCase <info> {
|
54
|
+
.Webspicy::Web::Specification::TestCase <info> {
|
61
55
|
description :? String
|
62
56
|
dress_params :? Boolean
|
63
57
|
params :? Params
|
@@ -77,8 +71,6 @@ TestCase =
|
|
77
71
|
tags :? [Tag]
|
78
72
|
}
|
79
73
|
|
80
|
-
Params = .Array|.Hash
|
81
|
-
|
82
74
|
StatusRange = .Webspicy::Support::StatusRange
|
83
75
|
<int> Integer
|
84
76
|
<str> String(s | s =~ /^\dxx$/ )
|
@@ -36,11 +36,14 @@ module Webspicy
|
|
36
36
|
response.body.to_s
|
37
37
|
end
|
38
38
|
|
39
|
-
def
|
39
|
+
def is_structured_output?
|
40
40
|
ct = response.content_type || test_case.expected_content_type
|
41
41
|
ct = ct.mime_type if ct.respond_to?(:mime_type)
|
42
|
-
|
43
|
-
|
42
|
+
ct =~ /json/
|
43
|
+
end
|
44
|
+
|
45
|
+
def loaded_output
|
46
|
+
if is_structured_output?
|
44
47
|
raise "Body empty while expected" if raw_output.empty?
|
45
48
|
@loaded_output ||= ::JSON.parse(response.body)
|
46
49
|
else
|
@@ -50,6 +53,7 @@ module Webspicy
|
|
50
53
|
alias :loaded_body :loaded_output
|
51
54
|
|
52
55
|
def output
|
56
|
+
return loaded_output unless is_structured_output?
|
53
57
|
schema = is_expected_success? ? service.output_schema : service.error_schema
|
54
58
|
begin
|
55
59
|
schema.dress(loaded_output)
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Web
|
3
|
+
class Specification < Webspicy::Specification
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def info(raw)
|
7
|
+
new(raw)
|
8
|
+
end
|
9
|
+
|
10
|
+
def singleservice(raw)
|
11
|
+
converted = {
|
12
|
+
name: raw[:name] || "Unamed specification",
|
13
|
+
url: raw[:url],
|
14
|
+
services: [
|
15
|
+
Webspicy::Web.service(raw.reject{|k| k==:url or k==:name }, Webspicy.current_scope)
|
16
|
+
]
|
17
|
+
}
|
18
|
+
info(converted)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def url
|
23
|
+
@raw[:url]
|
24
|
+
end
|
25
|
+
|
26
|
+
def url_pattern
|
27
|
+
@url_pattern ||= Mustermann.new(url, type: :template)
|
28
|
+
end
|
29
|
+
|
30
|
+
def url_placeholders
|
31
|
+
url.scan(/\{([a-zA-Z]+(\.[a-zA-Z]+)*)\}/).map{|x| x.first }
|
32
|
+
end
|
33
|
+
|
34
|
+
def instantiate_url(params)
|
35
|
+
url, rest = self.url, params.dup
|
36
|
+
url_placeholders.each do |placeholder|
|
37
|
+
value, rest = extract_placeholder_value(rest, placeholder)
|
38
|
+
url = url.gsub("{#{placeholder}}", value.to_s)
|
39
|
+
end
|
40
|
+
[ url, rest ]
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_singleservice
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def extract_placeholder_value(params, placeholder, split = nil)
|
50
|
+
return extract_placeholder_value(params, placeholder, placeholder.split(".")) unless split
|
51
|
+
|
52
|
+
key = [ split.first, split.first.to_sym ].find{|k| params.has_key?(k) }
|
53
|
+
raise "Missing URL parameter `#{placeholder}`" unless key
|
54
|
+
|
55
|
+
if split.size == 1
|
56
|
+
[ params[key], params.dup.delete_if{|k| k == key } ]
|
57
|
+
else
|
58
|
+
value, rest = extract_placeholder_value(params[key], placeholder, split[1..-1])
|
59
|
+
[ value, params.merge(key => rest) ]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end # class Specification
|
64
|
+
end # module Web
|
65
|
+
end # module Webspicy
|
66
|
+
require_relative 'specification/service'
|
67
|
+
require_relative 'specification/test_case'
|
68
|
+
require_relative 'specification/file_upload'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Web
|
3
|
+
class Specification
|
4
|
+
class FileUpload
|
5
|
+
|
6
|
+
def initialize(raw)
|
7
|
+
@path = raw[:path]
|
8
|
+
@content_type = raw[:content_type]
|
9
|
+
@param_name = raw[:param_name] || "file"
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :path, :content_type, :param_name
|
13
|
+
|
14
|
+
def self.info(raw)
|
15
|
+
new(raw)
|
16
|
+
end
|
17
|
+
|
18
|
+
def locate(specification)
|
19
|
+
FileUpload.new({
|
20
|
+
path: specification.locate(path),
|
21
|
+
content_type: content_type
|
22
|
+
})
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_info
|
26
|
+
{ path: path.to_s,
|
27
|
+
content_type: content_type,
|
28
|
+
param_name: param_name }
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"FileUpload(#{to_info})"
|
33
|
+
end
|
34
|
+
alias :inspect :to_s
|
35
|
+
|
36
|
+
end # class FileUpload
|
37
|
+
end # class Specification
|
38
|
+
end # module Web
|
39
|
+
end # module Webspicy
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Web
|
3
|
+
class Specification
|
4
|
+
class TestCase < Webspicy::Specification::TestCase
|
5
|
+
|
6
|
+
def headers
|
7
|
+
@raw[:headers] ||= {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def dress_params
|
11
|
+
@raw.fetch(:dress_params){ true }
|
12
|
+
end
|
13
|
+
alias :dress_params? :dress_params
|
14
|
+
|
15
|
+
def params
|
16
|
+
@raw[:params] || {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def body
|
20
|
+
@raw[:body]
|
21
|
+
end
|
22
|
+
|
23
|
+
def file_upload
|
24
|
+
@raw[:file_upload]
|
25
|
+
end
|
26
|
+
|
27
|
+
def located_file_upload
|
28
|
+
file_upload ? file_upload.locate(specification) : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def expected_content_type
|
32
|
+
expected[:content_type]
|
33
|
+
end
|
34
|
+
|
35
|
+
def expected_status
|
36
|
+
expected[:status]
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_expected_status?(status)
|
40
|
+
expected_status === status
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_expected_status?
|
44
|
+
not expected[:status].nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
def expected_headers
|
48
|
+
expected[:headers] || {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_expected_headers?
|
52
|
+
!expected_headers.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
end # class TestCase
|
56
|
+
end # class Specification
|
57
|
+
end # module Web
|
58
|
+
end # module Webspicy
|
@@ -3,11 +3,17 @@ module Webspicy
|
|
3
3
|
class Configuration
|
4
4
|
describe Scope, "expand_example" do
|
5
5
|
|
6
|
-
|
6
|
+
let(:config){
|
7
|
+
Configuration.new(Path.dir)
|
8
|
+
}
|
9
|
+
|
10
|
+
subject{
|
11
|
+
Scope.new(config).send(:expand_example, service, example)
|
12
|
+
}
|
7
13
|
|
8
14
|
context 'when the service has no default example' do
|
9
15
|
let(:service) {
|
10
|
-
Webspicy.service({
|
16
|
+
Webspicy::Web.service({
|
11
17
|
method: "GET",
|
12
18
|
description: "Test service",
|
13
19
|
preconditions: "Foo",
|
@@ -18,7 +24,7 @@ module Webspicy
|
|
18
24
|
}
|
19
25
|
|
20
26
|
let(:example) {
|
21
|
-
Webspicy.test_case({
|
27
|
+
Webspicy::Web.test_case({
|
22
28
|
description: "Hello world"
|
23
29
|
})
|
24
30
|
}
|
@@ -30,7 +36,7 @@ module Webspicy
|
|
30
36
|
|
31
37
|
context 'when the service has a default example' do
|
32
38
|
let(:service) {
|
33
|
-
Webspicy.service({
|
39
|
+
Webspicy::Web.service({
|
34
40
|
method: "GET",
|
35
41
|
description: "Test service",
|
36
42
|
preconditions: "Foo",
|
@@ -44,7 +50,7 @@ module Webspicy
|
|
44
50
|
}
|
45
51
|
|
46
52
|
let(:example) {
|
47
|
-
Webspicy.test_case({
|
53
|
+
Webspicy::Web.test_case({
|
48
54
|
description: "Hello world",
|
49
55
|
expected: { content_type: "application/json" }
|
50
56
|
})
|
@@ -15,13 +15,13 @@ module Webspicy
|
|
15
15
|
|
16
16
|
describe "instrument" do
|
17
17
|
it 'injects the headers' do
|
18
|
-
tc = TestCase.new({})
|
18
|
+
tc = Web::Specification::TestCase.new({})
|
19
19
|
instrument(tc)
|
20
20
|
expect(tc.headers['Accept']).to eql("application/json")
|
21
21
|
end
|
22
22
|
|
23
23
|
it 'keeps original headers unchanged' do
|
24
|
-
tc = TestCase.new({
|
24
|
+
tc = Web::Specification::TestCase.new({
|
25
25
|
headers: {
|
26
26
|
'Content-Type' => 'text/plain'
|
27
27
|
}
|
@@ -32,7 +32,7 @@ module Webspicy
|
|
32
32
|
end
|
33
33
|
|
34
34
|
it 'has low precedence' do
|
35
|
-
tc = TestCase.new({
|
35
|
+
tc = Web::Specification::TestCase.new({
|
36
36
|
headers: {
|
37
37
|
'Accept' => 'text/plain'
|
38
38
|
}
|