webspicy 0.1.0.pre.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +2 -0
- data/LICENSE.md +22 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/examples/restful/Gemfile +5 -0
- data/examples/restful/Gemfile.lock +69 -0
- data/examples/restful/Rakefile +21 -0
- data/examples/restful/app.rb +32 -0
- data/examples/restful/webspicy/schema.fio +4 -0
- data/examples/restful/webspicy/todo/getTodo.yml +52 -0
- data/examples/restful/webspicy/todo/getTodos.yml +39 -0
- data/lib/webspicy/checker.rb +26 -0
- data/lib/webspicy/client/http_client.rb +70 -0
- data/lib/webspicy/client.rb +20 -0
- data/lib/webspicy/configuration.rb +168 -0
- data/lib/webspicy/formaldoc.fio +47 -0
- data/lib/webspicy/resource/service/invocation.rb +158 -0
- data/lib/webspicy/resource/service/test_case.rb +74 -0
- data/lib/webspicy/resource/service.rb +49 -0
- data/lib/webspicy/resource.rb +43 -0
- data/lib/webspicy/scope.rb +113 -0
- data/lib/webspicy/tester/asserter.rb +94 -0
- data/lib/webspicy/tester/assertions.rb +103 -0
- data/lib/webspicy/tester.rb +96 -0
- data/lib/webspicy/version.rb +8 -0
- data/lib/webspicy.rb +112 -0
- data/spec/unit/resource/service/test_dress_params.rb +34 -0
- data/spec/unit/resource/test_instantiate_url.rb +20 -0
- data/spec/unit/resource/test_url_placeholders.rb +16 -0
- data/spec/unit/scope/test_each_resource.rb +59 -0
- data/spec/unit/scope/test_each_service.rb +51 -0
- data/spec/unit/scope/test_to_real_url.rb +75 -0
- data/spec/unit/spec_helper.rb +28 -0
- data/spec/unit/test_configuration.rb +84 -0
- data/spec/unit/tester/test_assertions.rb +108 -0
- data/tasks/test.rake +27 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b34d2485758616a81425a1f39fd0e5350fea1799
|
4
|
+
data.tar.gz: 8756493dea8e750857c172399dcebe2edcc2a76b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ee96045a18f309ca39859a8459cc2cf11b1dd02d3fbc9a45186f7f5ef45af46b5121f5626303e52eca886196b8baa0eb34c17e57702e5408514092a78c089de
|
7
|
+
data.tar.gz: 83db0b47519892d12acc7b73ff4bf2ec97642ffebb680d2f0535915c6f11321172656a0dee2ddd62c33317cd9045b0bb8104f28a997f9c7f92c2293fe39a009f
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# The MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2017 - Enspirit SPRL (Bernard Lambeau)
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../..
|
3
|
+
specs:
|
4
|
+
webspicy (0.0.1)
|
5
|
+
finitio (~> 0.5.2)
|
6
|
+
http (~> 0.5)
|
7
|
+
path (~> 1.3)
|
8
|
+
rspec (~> 3.6)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
addressable (2.5.1)
|
14
|
+
public_suffix (~> 2.0, >= 2.0.2)
|
15
|
+
citrus (3.0.2)
|
16
|
+
diff-lcs (1.3)
|
17
|
+
domain_name (0.5.20170404)
|
18
|
+
unf (>= 0.0.5, < 1.0.0)
|
19
|
+
finitio (0.5.2)
|
20
|
+
citrus (>= 2.4, < 4.0)
|
21
|
+
http (0.9.9)
|
22
|
+
addressable (~> 2.3)
|
23
|
+
http-cookie (~> 1.0)
|
24
|
+
http-form_data (~> 1.0.1)
|
25
|
+
http_parser.rb (~> 0.6.0)
|
26
|
+
http-cookie (1.0.3)
|
27
|
+
domain_name (~> 0.5)
|
28
|
+
http-form_data (1.0.3)
|
29
|
+
http_parser.rb (0.6.0)
|
30
|
+
mustermann (1.0.0)
|
31
|
+
path (1.3.3)
|
32
|
+
public_suffix (2.0.5)
|
33
|
+
rack (2.0.3)
|
34
|
+
rack-protection (2.0.0)
|
35
|
+
rack
|
36
|
+
rake (10.5.0)
|
37
|
+
rspec (3.6.0)
|
38
|
+
rspec-core (~> 3.6.0)
|
39
|
+
rspec-expectations (~> 3.6.0)
|
40
|
+
rspec-mocks (~> 3.6.0)
|
41
|
+
rspec-core (3.6.0)
|
42
|
+
rspec-support (~> 3.6.0)
|
43
|
+
rspec-expectations (3.6.0)
|
44
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
45
|
+
rspec-support (~> 3.6.0)
|
46
|
+
rspec-mocks (3.6.0)
|
47
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
48
|
+
rspec-support (~> 3.6.0)
|
49
|
+
rspec-support (3.6.0)
|
50
|
+
sinatra (2.0.0)
|
51
|
+
mustermann (~> 1.0)
|
52
|
+
rack (~> 2.0)
|
53
|
+
rack-protection (= 2.0.0)
|
54
|
+
tilt (~> 2.0)
|
55
|
+
tilt (2.0.7)
|
56
|
+
unf (0.1.4)
|
57
|
+
unf_ext
|
58
|
+
unf_ext (0.0.7.4)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
rake (~> 10)
|
65
|
+
sinatra (~> 2.0)
|
66
|
+
webspicy!
|
67
|
+
|
68
|
+
BUNDLED WITH
|
69
|
+
1.14.6
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'webspicy'
|
2
|
+
|
3
|
+
namespace :webspicy do
|
4
|
+
|
5
|
+
config = Webspicy::Configuration.new do |c|
|
6
|
+
c.host = "http://127.0.0.1:4567"
|
7
|
+
c.add_folder Path.dir/"webspicy"
|
8
|
+
end
|
9
|
+
|
10
|
+
task :check do
|
11
|
+
Webspicy::Checker.new(config).call
|
12
|
+
end
|
13
|
+
|
14
|
+
task :test do
|
15
|
+
Webspicy::Tester.new(config).call
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
task :test => :"webspicy:test"
|
21
|
+
task :default => :test
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sinatra'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
TODOLIST = [
|
5
|
+
{
|
6
|
+
id: 1,
|
7
|
+
description: "Refactor the framework"
|
8
|
+
},
|
9
|
+
{
|
10
|
+
id: 2,
|
11
|
+
description: "Write documentation"
|
12
|
+
}
|
13
|
+
]
|
14
|
+
|
15
|
+
disable :show_exceptions
|
16
|
+
enable :raise_errors
|
17
|
+
|
18
|
+
get '/todo/' do
|
19
|
+
content_type :json
|
20
|
+
TODOLIST.to_json
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/todo/:id' do |id|
|
24
|
+
content_type :json
|
25
|
+
todo = TODOLIST.find{|todo| todo[:id] == Integer(id) }
|
26
|
+
if todo.nil?
|
27
|
+
status 404
|
28
|
+
{error: "No such todo"}.to_json
|
29
|
+
else
|
30
|
+
todo.to_json
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
---
|
2
|
+
name: |-
|
3
|
+
Todo
|
4
|
+
|
5
|
+
url: |-
|
6
|
+
/todo/{id}
|
7
|
+
|
8
|
+
services:
|
9
|
+
- method: |-
|
10
|
+
GET
|
11
|
+
|
12
|
+
description: |-
|
13
|
+
Returns a single todo item
|
14
|
+
|
15
|
+
preconditions: |-
|
16
|
+
|
17
|
+
input_schema: |-
|
18
|
+
{
|
19
|
+
id: Integer
|
20
|
+
}
|
21
|
+
|
22
|
+
output_schema: |-
|
23
|
+
Todo
|
24
|
+
|
25
|
+
error_schema: |-
|
26
|
+
{
|
27
|
+
error: String
|
28
|
+
}
|
29
|
+
|
30
|
+
examples:
|
31
|
+
|
32
|
+
- description: |-
|
33
|
+
when requested on an existing TODO
|
34
|
+
params:
|
35
|
+
id: 1
|
36
|
+
expected:
|
37
|
+
content_type: application/json
|
38
|
+
status: 200
|
39
|
+
assert:
|
40
|
+
- "pathFD('', id: 1)"
|
41
|
+
|
42
|
+
counterexamples:
|
43
|
+
|
44
|
+
- description: |-
|
45
|
+
when requested on an unexisting TODO
|
46
|
+
params:
|
47
|
+
id: 999254654
|
48
|
+
expected:
|
49
|
+
content_type: application/json
|
50
|
+
status: 404
|
51
|
+
assert:
|
52
|
+
- "pathFD('', error: 'No such todo')"
|
@@ -0,0 +1,39 @@
|
|
1
|
+
---
|
2
|
+
name: |-
|
3
|
+
Todo
|
4
|
+
|
5
|
+
url: |-
|
6
|
+
/todo/
|
7
|
+
|
8
|
+
services:
|
9
|
+
- method: |-
|
10
|
+
GET
|
11
|
+
|
12
|
+
description: |-
|
13
|
+
Returns the list of todo items
|
14
|
+
|
15
|
+
preconditions: |-
|
16
|
+
|
17
|
+
input_schema: |-
|
18
|
+
{
|
19
|
+
}
|
20
|
+
|
21
|
+
output_schema: |-
|
22
|
+
[Todo]
|
23
|
+
|
24
|
+
error_schema: |-
|
25
|
+
{
|
26
|
+
}
|
27
|
+
|
28
|
+
examples:
|
29
|
+
|
30
|
+
- description: |-
|
31
|
+
when requested
|
32
|
+
params: {}
|
33
|
+
expected:
|
34
|
+
content_type: application/json
|
35
|
+
status: 200
|
36
|
+
assert:
|
37
|
+
- notEmpty
|
38
|
+
|
39
|
+
counterexamples: []
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class Checker
|
3
|
+
|
4
|
+
def initialize(config)
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def call
|
10
|
+
Webspicy.with_scope_for(config) do |scope|
|
11
|
+
client = scope.get_client
|
12
|
+
scope.each_resource_file do |file, folder|
|
13
|
+
RSpec.describe file.relative_to(folder).to_s do
|
14
|
+
|
15
|
+
it 'meets the formal doc data schema' do
|
16
|
+
Webspicy.resource(file.load, file)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
RSpec::Core::Runner.run config.rspec_options
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class HttpClient < Client
|
3
|
+
|
4
|
+
def call(test_case, service, resource)
|
5
|
+
# Instantiate the parameters
|
6
|
+
headers = test_case.headers
|
7
|
+
params = test_case.dress_params? ? service.dress_params(test_case.params) : test_case.params
|
8
|
+
|
9
|
+
# Instantiate the url and strip parameters
|
10
|
+
url, params = resource.instantiate_url(params)
|
11
|
+
|
12
|
+
# Globalize the URL if required
|
13
|
+
url = scope.to_real_url(url)
|
14
|
+
|
15
|
+
# Invoke the service now
|
16
|
+
api = Api.new
|
17
|
+
api.public_send(service.method.to_s.downcase.to_sym, url, params, headers)
|
18
|
+
|
19
|
+
# Return the result
|
20
|
+
Resource::Service::Invocation.new(service, test_case, api.last_response)
|
21
|
+
end
|
22
|
+
|
23
|
+
class Api
|
24
|
+
|
25
|
+
attr_reader :last_response
|
26
|
+
|
27
|
+
def get(url, params, headers = nil)
|
28
|
+
headers, url = headers_and_url_for(url, params, headers)
|
29
|
+
|
30
|
+
Webspicy.info("GET #{url} -- #{params.inspect}")
|
31
|
+
|
32
|
+
@last_response = HTTP[headers].get(url, params: params)
|
33
|
+
|
34
|
+
Webspicy.debug("Headers: #{@last_response.headers.to_hash}")
|
35
|
+
Webspicy.debug("Response: #{@last_response.body}")
|
36
|
+
end
|
37
|
+
|
38
|
+
def post(url, params, headers = nil)
|
39
|
+
headers, url = headers_and_url_for(url, params, headers)
|
40
|
+
|
41
|
+
Webspicy.info("POST #{url} -- #{params.inspect}")
|
42
|
+
|
43
|
+
@last_response = HTTP[headers].post(url, body: params.to_json)
|
44
|
+
|
45
|
+
Webspicy.debug("Headers: #{@last_response.headers.to_hash}")
|
46
|
+
Webspicy.debug("Response: #{@last_response.body}")
|
47
|
+
end
|
48
|
+
|
49
|
+
def post_form(url, params, headers = nil)
|
50
|
+
headers, url = headers_and_url_for(url, params, headers)
|
51
|
+
|
52
|
+
Webspicy.info("POST #{url} -- #{params.inspect}")
|
53
|
+
|
54
|
+
@last_response = HTTP[headers].post(url, form: params)
|
55
|
+
|
56
|
+
Webspicy.debug("Headers: #{@last_response.headers.to_hash}")
|
57
|
+
Webspicy.debug("Response: #{@last_response.body}")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def headers_and_url_for(url, params, headers)
|
63
|
+
headers = headers || {}
|
64
|
+
[ headers, url ]
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class Client
|
3
|
+
|
4
|
+
def initialize(scope)
|
5
|
+
@scope = scope
|
6
|
+
end
|
7
|
+
attr_reader :scope
|
8
|
+
|
9
|
+
def config
|
10
|
+
scope.config
|
11
|
+
end
|
12
|
+
|
13
|
+
def before(*args, &bl)
|
14
|
+
config.before_listeners.each do |beach|
|
15
|
+
beach.call(*args, &bl)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@folders = []
|
6
|
+
@before_listeners = []
|
7
|
+
@rspec_options = default_rspec_options
|
8
|
+
@run_counterexamples = default_run_counterexamples
|
9
|
+
@file_filter = default_file_filter
|
10
|
+
@service_filter = default_service_filter
|
11
|
+
@client = HttpClient
|
12
|
+
yield(self) if block_given?
|
13
|
+
end
|
14
|
+
|
15
|
+
# Adds a folder to the list of folders where test case definitions are
|
16
|
+
# to be found.
|
17
|
+
def add_folder(folder)
|
18
|
+
folder = Path(folder)
|
19
|
+
raise "Folder `#{folder}` does not exists" unless folder.exists? && folder.directory?
|
20
|
+
@folders << folder
|
21
|
+
end
|
22
|
+
attr_reader :folders
|
23
|
+
|
24
|
+
# Sets whether counter examples have to be ran or not.
|
25
|
+
def run_counterexamples=(run_counterexamples)
|
26
|
+
@run_counterexamples = run_counterexamples
|
27
|
+
end
|
28
|
+
attr_reader :run_counterexamples
|
29
|
+
|
30
|
+
# Whether counter examples must be ran or not.
|
31
|
+
def run_counterexamples?
|
32
|
+
@run_counterexamples
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the defaut value for run_counterexamples
|
36
|
+
def default_run_counterexamples
|
37
|
+
ENV['ROBUST'].nil? || ENV['ROBUST'] != 'no'
|
38
|
+
end
|
39
|
+
private :default_run_counterexamples
|
40
|
+
|
41
|
+
# Installs a host (resolver).
|
42
|
+
#
|
43
|
+
# The host resolver is responsible from transforming URLs found in
|
44
|
+
# .yml test files to an absolute URL invoked by the client. Supported
|
45
|
+
# values are:
|
46
|
+
#
|
47
|
+
# - String: taken as a prefix for all relative URLs. Using this option
|
48
|
+
# lets specify all webservices through relative URLs and having the
|
49
|
+
# host itself as global configuration variable.
|
50
|
+
# - Proc: all URLs are passed to the proc, relative and absolute ones.
|
51
|
+
# The result of the proc is used as URL to use in practice.
|
52
|
+
#
|
53
|
+
# When no host provider is provided, all URLs are expected to be absolute
|
54
|
+
# URLs, otherwise an error will be thrown at runtime.
|
55
|
+
def host=(host)
|
56
|
+
@host = host
|
57
|
+
end
|
58
|
+
attr_reader :host
|
59
|
+
|
60
|
+
# Installs a file filter.
|
61
|
+
#
|
62
|
+
# A file filter can be added to restrict the scope attention only to the
|
63
|
+
# files that match the filter installed. Supported values are:
|
64
|
+
#
|
65
|
+
# - Proc: each file (a Path instance) is passed in turn. Only files for
|
66
|
+
# which a truthy value is returned will be considered by the scope.
|
67
|
+
# - Regexp: the path of each file is matched against the regexp. Only files
|
68
|
+
# that match are considered by the scope.
|
69
|
+
# - ===: any instance responding to `===` can be used as a matcher, following
|
70
|
+
# Ruby conventions. The match is done on a Path instance.
|
71
|
+
#
|
72
|
+
def file_filter=(file_filter)
|
73
|
+
@file_filter = file_filter
|
74
|
+
end
|
75
|
+
attr_reader :file_filter
|
76
|
+
|
77
|
+
# Returns the default file filter to use.
|
78
|
+
#
|
79
|
+
# By default no file filter is set, unless a RESOURCE environment variable is
|
80
|
+
# set. In that case, a file filter is set that matches the file name to the
|
81
|
+
# variable value, through a regular expression.
|
82
|
+
def default_file_filter
|
83
|
+
ENV['RESOURCE'] ? Regexp.compile(ENV['RESOURCE']) : nil
|
84
|
+
end
|
85
|
+
private :default_file_filter
|
86
|
+
|
87
|
+
# Installs a service filter.
|
88
|
+
#
|
89
|
+
# A service filter can be added to restrict the scope attention only to the
|
90
|
+
# services that match the filter installed. Supported values are:
|
91
|
+
#
|
92
|
+
# - Proc: each service is passed in turn. Only services for which a truthy value
|
93
|
+
# is returned will be considered by the scope.
|
94
|
+
# - ===: any instance responding to `===` can be used as a matcher, following
|
95
|
+
# Ruby conventions. The match is done on a Service instance.
|
96
|
+
#
|
97
|
+
def service_filter=(service_filter)
|
98
|
+
@service_filter = service_filter
|
99
|
+
end
|
100
|
+
attr_reader :service_filter
|
101
|
+
|
102
|
+
# Returns the default service filters.
|
103
|
+
#
|
104
|
+
# By default no filter is set unless a METHOD environment variable is set.
|
105
|
+
# In that case, a service filter is returned that filters the services whose
|
106
|
+
# HTTP method match the variable value.
|
107
|
+
def default_service_filter
|
108
|
+
ENV['METHOD'] ? ->(s){ s.method.to_s.downcase == ENV['METHOD'].downcase } : nil
|
109
|
+
end
|
110
|
+
private :default_service_filter
|
111
|
+
|
112
|
+
|
113
|
+
# Installs a client class to use to invoke web services for real.
|
114
|
+
#
|
115
|
+
# This configuration allows defining a subclass of Client to be used for
|
116
|
+
# actually invoking web services. Options are:
|
117
|
+
#
|
118
|
+
# - HttpClient: Uses the HTTP library to make real HTTP call to a web server.
|
119
|
+
#
|
120
|
+
# Note that this configuration variable expected a client *class*, not an
|
121
|
+
# instance
|
122
|
+
def client=(client)
|
123
|
+
@client = client
|
124
|
+
end
|
125
|
+
attr_reader :client
|
126
|
+
|
127
|
+
# Installs a listener that will be called before each web service invocation.
|
128
|
+
#
|
129
|
+
# The `listener` must respond to `call`.
|
130
|
+
def before_each(&listener)
|
131
|
+
raise "Must respond to call" unless listener.respond_to?(:call)
|
132
|
+
@before_listeners << listener
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the list of listeners that must be called before each web service
|
136
|
+
# invocation.
|
137
|
+
def before_listeners
|
138
|
+
@before_listeners
|
139
|
+
end
|
140
|
+
|
141
|
+
# Allows setting the options passed at RSpec, which is used by both the runner
|
142
|
+
# and checker classes.
|
143
|
+
#
|
144
|
+
# `options` is supposed to be valid RSpec options, to be passed at
|
145
|
+
# `RSpec::Core::Runner.run`
|
146
|
+
def rspec_options=(options)
|
147
|
+
@rspec_options = options
|
148
|
+
end
|
149
|
+
attr_reader :rspec_options
|
150
|
+
|
151
|
+
# Returns the default rspec options.
|
152
|
+
#
|
153
|
+
# By default rspec colors are enabled and the format set to 'documentation'.
|
154
|
+
# The following environment variables <-> rspec options are supported:
|
155
|
+
#
|
156
|
+
# - FAILFAST <-> --fail-fast
|
157
|
+
#
|
158
|
+
def default_rspec_options
|
159
|
+
options = ["--color", "--format=documentation"]
|
160
|
+
if ENV['FAILFAST']
|
161
|
+
options << (ENV['FAILFAST'] == 'no' ? "--no-fail-fast" : "--fail-fast=#{ENV['FAILFAST']}")
|
162
|
+
end
|
163
|
+
options
|
164
|
+
end
|
165
|
+
private :default_rspec_options
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
Method =
|
2
|
+
String( s | s =~ /^(GET|POST|POST_FORM|PUT|DELETE|PATCH)$/ )
|
3
|
+
|
4
|
+
Schema =
|
5
|
+
.Finitio::System <fio> String
|
6
|
+
\( s | ::Webspicy.schema(s) )
|
7
|
+
\( s | raise "Unsupported" )
|
8
|
+
|
9
|
+
Resource =
|
10
|
+
.Webspicy::Resource <info> {
|
11
|
+
name: String
|
12
|
+
url: String
|
13
|
+
services: [Service]
|
14
|
+
}
|
15
|
+
|
16
|
+
Service =
|
17
|
+
.Webspicy::Resource::Service <info> {
|
18
|
+
method : Method
|
19
|
+
description : String
|
20
|
+
preconditions : String
|
21
|
+
postconditions :? String
|
22
|
+
input_schema : Schema
|
23
|
+
output_schema : Schema
|
24
|
+
error_schema : Schema
|
25
|
+
blackbox :? String
|
26
|
+
examples :? [TestCase]
|
27
|
+
counterexamples :? [TestCase]
|
28
|
+
}
|
29
|
+
|
30
|
+
TestCase =
|
31
|
+
.Webspicy::Resource::Service::TestCase <info> {
|
32
|
+
description : String
|
33
|
+
dress_params :? Boolean
|
34
|
+
params : Params
|
35
|
+
headers :? .Hash
|
36
|
+
seeds :? String
|
37
|
+
requester :? String
|
38
|
+
expected: {
|
39
|
+
status : Integer
|
40
|
+
content_type :? String
|
41
|
+
error :? String
|
42
|
+
headers :? .Hash
|
43
|
+
}
|
44
|
+
assert :? [String]
|
45
|
+
}
|
46
|
+
|
47
|
+
Params = .Array|.Hash
|