webspicy 0.16.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -5
- data/examples/restful/webspicy/config.rb +1 -0
- data/examples/restful/webspicy/{todo/deleteTodo.yml → formaldef/todo/_one/delete.yml} +3 -0
- data/examples/restful/webspicy/{todo/getTodoSingleServiceFormat.yml → formaldef/todo/_one/get.simpler.yml} +0 -0
- data/examples/restful/webspicy/{todo/getTodo.yml → formaldef/todo/_one/get.yml} +0 -0
- data/examples/restful/webspicy/{todo/patchTodo.yml → formaldef/todo/_one/patch.yml} +0 -0
- data/examples/restful/webspicy/{todo/putTodo.yml → formaldef/todo/_one/put.yml} +0 -0
- data/examples/restful/webspicy/{todo/getTodos.yml → formaldef/todo/get.yml} +0 -0
- data/examples/restful/webspicy/{todo → formaldef/todo}/options.yml +0 -0
- data/examples/restful/webspicy/{todo/postCsv.yml → formaldef/todo/post.csv.yml} +0 -0
- data/examples/restful/webspicy/{todo/postFile.yml → formaldef/todo/post.file.yml} +0 -0
- data/examples/restful/webspicy/{todo/postTodos.yml → formaldef/todo/post.yml} +0 -0
- data/examples/restful/webspicy/{todo → formaldef/todo}/todos.csv +0 -0
- data/examples/restful/webspicy/support/todo_not_removed.rb +21 -0
- data/examples/restful/webspicy/support/todo_removed.rb +5 -3
- data/examples/website/specification/get-http.yml +20 -24
- data/examples/website/specification/get-https.yml +20 -24
- data/lib/webspicy.rb +4 -3
- data/lib/webspicy/checker.rb +5 -20
- data/lib/webspicy/configuration.rb +9 -0
- data/lib/webspicy/configuration/scope.rb +0 -8
- data/lib/webspicy/formaldoc.fio +8 -6
- data/lib/webspicy/mocker/config.ru +5 -0
- data/lib/webspicy/rspec/checker.rb +2 -0
- data/lib/webspicy/rspec/checker/rspec_checker.rb +24 -0
- data/lib/webspicy/rspec/support/rspec_runnable.rb +27 -0
- data/lib/webspicy/rspec/tester.rb +4 -0
- data/lib/webspicy/{tester → rspec/tester}/rspec_asserter.rb +24 -11
- data/lib/webspicy/{tester → rspec/tester}/rspec_matchers.rb +10 -0
- data/lib/webspicy/rspec/tester/rspec_tester.rb +63 -0
- data/lib/webspicy/specification.rb +10 -10
- data/lib/webspicy/specification/errcondition.rb +16 -0
- data/lib/webspicy/specification/precondition/robust_to_invalid_input.rb +1 -1
- data/lib/webspicy/specification/service.rb +27 -19
- data/lib/webspicy/specification/test_case.rb +3 -9
- data/lib/webspicy/support.rb +1 -0
- data/lib/webspicy/support/data_object.rb +25 -0
- data/lib/webspicy/tester.rb +4 -78
- data/lib/webspicy/tester/asserter.rb +9 -4
- data/lib/webspicy/tester/assertions.rb +8 -9
- data/lib/webspicy/tester/failure.rb +6 -0
- data/lib/webspicy/tester/invocation.rb +8 -156
- data/lib/webspicy/version.rb +1 -1
- data/spec/unit/configuration/scope/test_each_service.rb +2 -2
- data/spec/unit/configuration/scope/test_each_specification.rb +7 -7
- data/spec/unit/test_configuration.rb +1 -1
- data/spec/unit/tester/test_asserter.rb +198 -3
- data/spec/unit/tester/test_assertions.rb +8 -6
- metadata +33 -25
- data/LICENSE.md +0 -22
- data/examples/restful/Gemfile.lock +0 -105
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2eaf855f36546e0f538b47c50d6cb612a741807057f82172a93bff42542b326f
|
4
|
+
data.tar.gz: b2e892ebf4d30e04d843df175d01ac77e8e65e145b7e6a88b4eccc0c24614ea5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6c77544f40a49028508709e8be6e7f7b95d979212416bcde782cdb5dd361f0091433d68e38c8908800851c629053c34f2c102ae41be8dea660f5ee51a839177
|
7
|
+
data.tar.gz: 8fb1d33bf31723292d39a2451a79fcca4e67c5fc57f37ade00e40290fbe12e3ebbd718fc619e7c2128fdae34e7c8e44a087649fb4199d8d3505d42ae3b0e982f
|
data/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
[![Build Status](https://travis-ci.com/enspirit/webspicy.svg?branch=master)](https://travis-ci.com/enspirit/webspicy)
|
1
2
|
# Webspicy
|
2
3
|
|
3
4
|
A specification and test framework for web services seen as black-box software
|
4
|
-
operations. Webspicy yields a better test coverage for a smaller testing effort
|
5
|
-
because software quality matters.
|
5
|
+
operations. Webspicy yields a better test coverage for a smaller testing effort.
|
6
6
|
|
7
7
|
See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
8
8
|
|
9
|
+
Have a look at `doc/*.md` for vocabulary and vision as well as `ROADMAP.md`.
|
10
|
+
|
9
11
|
## Features
|
10
12
|
|
11
13
|
* Declarative specification of HTTP web services + their tests
|
@@ -19,7 +21,7 @@ See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
|
19
21
|
your API design better.
|
20
22
|
|
21
23
|
* Formal and human-friendly data schema with strong data matching semantics,
|
22
|
-
thanks to finitio.io
|
24
|
+
thanks to [http://finitio.io](http://finitio.io)
|
23
25
|
|
24
26
|
* Test instrumentation and generation, based on PRE & POST contracts.
|
25
27
|
|
@@ -30,6 +32,10 @@ See webspicy in action and make the tutorial on https://yourbackendisbroken.dev
|
|
30
32
|
* Extra goodies: when a specification is written, it can also be used for
|
31
33
|
mocking the API, generating an openapi file, etc.
|
32
34
|
|
35
|
+
## Is this used on real-world cases?
|
36
|
+
|
37
|
+
Yes, `webspicy` is currently used on a dozen production components. Our biggest specification has 324 specification files for thousands of tests, 35% of them being generated.
|
38
|
+
|
33
39
|
## Getting started with the commandline
|
34
40
|
|
35
41
|
To install webspicy on your developer computer, install ruby then:
|
@@ -60,7 +66,7 @@ to use our `:tester` docker image. Just mount your test suite as a volume
|
|
60
66
|
in `/home/app` and you are good to go:
|
61
67
|
|
62
68
|
```
|
63
|
-
docker run -v path/to/tests:/formalspec enspirit/webspicy
|
69
|
+
docker run -v path/to/tests:/formalspec enspirit/webspicy:tester
|
64
70
|
```
|
65
71
|
|
66
72
|
If your plan is to test a backend that runs on your own machine (vs.
|
@@ -69,7 +75,7 @@ you will need to add some networking option, as shown below. Please
|
|
69
75
|
refer to Docker documentation.
|
70
76
|
|
71
77
|
```
|
72
|
-
docker run -v path/to/tests:/formalspec --network=host enspirit/webspicy
|
78
|
+
docker run -v path/to/tests:/formalspec --network=host enspirit/webspicy:tester
|
73
79
|
```
|
74
80
|
|
75
81
|
## Contributing
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class TodoNotRemoved
|
2
|
+
include Webspicy::Specification::Postcondition
|
3
|
+
|
4
|
+
def self.match(service, descr)
|
5
|
+
return TodoNotRemoved.new if descr =~ /If it existed, the todo has not been removed/
|
6
|
+
end
|
7
|
+
|
8
|
+
def check(invocation)
|
9
|
+
client, scope, test_case = invocation.client,
|
10
|
+
invocation.client.scope,
|
11
|
+
invocation.test_case
|
12
|
+
return if invocation.response.status == 404
|
13
|
+
id = test_case.params['id']
|
14
|
+
url = scope.to_real_url("/todo/#{id}", test_case){|url| url }
|
15
|
+
response = client.api.get(url, {}, {
|
16
|
+
"Accept" => "application/json"
|
17
|
+
})
|
18
|
+
return nil if response.status == 200
|
19
|
+
"Todo `#{id}` was not supposed to be deleted, it was not found"
|
20
|
+
end
|
21
|
+
end
|
@@ -6,9 +6,11 @@ class TodoRemoved
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def check(invocation)
|
9
|
-
client = invocation.client
|
10
|
-
|
11
|
-
|
9
|
+
client, scope, test_case = invocation.client,
|
10
|
+
invocation.client.scope,
|
11
|
+
invocation.test_case
|
12
|
+
id = test_case.params['id']
|
13
|
+
url = scope.to_real_url("/todo/#{id}", test_case){|url| url }
|
12
14
|
response = client.api.get(url, {}, {
|
13
15
|
"Accept" => "application/json"
|
14
16
|
})
|
@@ -1,34 +1,30 @@
|
|
1
1
|
---
|
2
|
-
name: |-
|
3
|
-
Your backend is broken website
|
4
|
-
|
5
2
|
url: |-
|
6
3
|
http://yourbackendisbroken.dev
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
GET
|
5
|
+
method: |-
|
6
|
+
GET
|
11
7
|
|
12
|
-
|
13
|
-
|
8
|
+
description: |-
|
9
|
+
Redirects to the https version
|
14
10
|
|
15
|
-
|
16
|
-
|
11
|
+
input_schema: |-
|
12
|
+
Any
|
17
13
|
|
18
|
-
|
19
|
-
|
14
|
+
output_schema: |-
|
15
|
+
Any
|
20
16
|
|
21
|
-
|
22
|
-
|
17
|
+
error_schema: |-
|
18
|
+
Any
|
23
19
|
|
24
|
-
|
20
|
+
examples:
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
- description: |-
|
23
|
+
it works
|
24
|
+
params:
|
25
|
+
id: 1
|
26
|
+
expected:
|
27
|
+
content_type: text/html
|
28
|
+
status: 3xx
|
29
|
+
headers:
|
30
|
+
Location: https://yourbackendisbroken.dev/?id=1
|
@@ -1,34 +1,30 @@
|
|
1
1
|
---
|
2
|
-
name: |-
|
3
|
-
Your backend is broken website
|
4
|
-
|
5
2
|
url: |-
|
6
3
|
https://yourbackendisbroken.dev
|
7
4
|
|
8
|
-
|
9
|
-
|
10
|
-
GET
|
5
|
+
method: |-
|
6
|
+
GET
|
11
7
|
|
12
|
-
|
13
|
-
|
8
|
+
description: |-
|
9
|
+
Returns the web page
|
14
10
|
|
15
|
-
|
16
|
-
|
11
|
+
input_schema: |-
|
12
|
+
Any
|
17
13
|
|
18
|
-
|
19
|
-
|
14
|
+
output_schema: |-
|
15
|
+
Any
|
20
16
|
|
21
|
-
|
22
|
-
|
17
|
+
error_schema: |-
|
18
|
+
Any
|
23
19
|
|
24
|
-
|
20
|
+
examples:
|
25
21
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
- description: |-
|
23
|
+
it works
|
24
|
+
params:
|
25
|
+
id: 1
|
26
|
+
expected:
|
27
|
+
content_type: text/html
|
28
|
+
status: 200
|
29
|
+
assert:
|
30
|
+
- notEmpty
|
data/lib/webspicy.rb
CHANGED
@@ -62,6 +62,7 @@ module Webspicy
|
|
62
62
|
raw = YAML.load(raw) if raw.is_a?(String)
|
63
63
|
with_scope(scope) do
|
64
64
|
r = FORMALDOC["Specification"].dress(raw)
|
65
|
+
r.config = scope.config
|
65
66
|
r.located_at!(file) if file
|
66
67
|
r
|
67
68
|
end
|
@@ -89,9 +90,9 @@ module Webspicy
|
|
89
90
|
module_function :test_case
|
90
91
|
|
91
92
|
def handle_finitio_error(ex, scope)
|
92
|
-
|
93
|
-
|
94
|
-
|
93
|
+
msg = "#{ex.message}:\n #{ex.root_cause.message}"
|
94
|
+
msg = Support::Colorize.colorize_error(msg, scope.config)
|
95
|
+
fatal(msg)
|
95
96
|
raise
|
96
97
|
end
|
97
98
|
module_function :handle_finitio_error
|
data/lib/webspicy/checker.rb
CHANGED
@@ -1,25 +1,10 @@
|
|
1
1
|
module Webspicy
|
2
2
|
class Checker
|
3
3
|
|
4
|
-
def
|
5
|
-
|
4
|
+
def self.new(*args, &bl)
|
5
|
+
require_relative 'rspec/checker'
|
6
|
+
RSpecChecker.new(*args, &bl)
|
6
7
|
end
|
7
|
-
attr_reader :config
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
scope.each_specification_file do |file, folder|
|
12
|
-
RSpec.describe file.relative_to(folder).to_s do
|
13
|
-
|
14
|
-
it 'meets the formal doc data schema' do
|
15
|
-
Webspicy.specification(file.load, file, scope)
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
RSpec::Core::Runner.run config.rspec_options
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
9
|
+
end # class Checker
|
10
|
+
end # module Webspicy
|
@@ -9,6 +9,7 @@ module Webspicy
|
|
9
9
|
@children = []
|
10
10
|
@preconditions = []
|
11
11
|
@postconditions = []
|
12
|
+
@errconditions = []
|
12
13
|
@listeners = Hash.new{|h,k| h[k] = [] }
|
13
14
|
@rspec_options = default_rspec_options
|
14
15
|
@run_examples = default_run_examples
|
@@ -126,6 +127,13 @@ module Webspicy
|
|
126
127
|
attr_accessor :postconditions
|
127
128
|
protected :postconditions=
|
128
129
|
|
130
|
+
# Registers an errcondition matcher
|
131
|
+
def errcondition(clazz)
|
132
|
+
errconditions << clazz
|
133
|
+
end
|
134
|
+
attr_accessor :errconditions
|
135
|
+
protected :errconditions=
|
136
|
+
|
129
137
|
# Returns whether this configuration has children configurations or not
|
130
138
|
def has_children?
|
131
139
|
!children.empty?
|
@@ -422,6 +430,7 @@ module Webspicy
|
|
422
430
|
d.children = []
|
423
431
|
d.preconditions = self.preconditions.dup
|
424
432
|
d.postconditions = self.postconditions.dup
|
433
|
+
d.errconditions = self.errconditions.dup
|
425
434
|
d.rspec_options = self.rspec_options.dup
|
426
435
|
d.listeners = LISTENER_KINDS.inject({}){|ls,kind|
|
427
436
|
ls.merge(kind => self.listeners(kind).dup)
|
data/lib/webspicy/formaldoc.fio
CHANGED
@@ -18,18 +18,14 @@ FileUpload =
|
|
18
18
|
}
|
19
19
|
|
20
20
|
Specification = .Webspicy::Specification
|
21
|
-
<info> {
|
22
|
-
name: String
|
23
|
-
url: String
|
24
|
-
services: [Service]
|
25
|
-
}
|
26
21
|
<singleservice> {
|
27
22
|
name :? String
|
28
|
-
url :
|
23
|
+
url : String
|
29
24
|
method : Method
|
30
25
|
description : String
|
31
26
|
preconditions :? [String]|String
|
32
27
|
postconditions :? [String]|String
|
28
|
+
errconditions :? [String]|String
|
33
29
|
input_schema : Schema
|
34
30
|
output_schema : Schema
|
35
31
|
error_schema : Schema
|
@@ -38,6 +34,11 @@ Specification = .Webspicy::Specification
|
|
38
34
|
examples :? [TestCase]
|
39
35
|
counterexamples :? [TestCase]
|
40
36
|
}
|
37
|
+
<info> {
|
38
|
+
name: String
|
39
|
+
url: String
|
40
|
+
services: [Service]
|
41
|
+
}
|
41
42
|
|
42
43
|
Service =
|
43
44
|
.Webspicy::Specification::Service <info> {
|
@@ -45,6 +46,7 @@ Service =
|
|
45
46
|
description : String
|
46
47
|
preconditions :? [String]|String
|
47
48
|
postconditions :? [String]|String
|
49
|
+
errconditions :? [String]|String
|
48
50
|
input_schema : Schema
|
49
51
|
output_schema : Schema
|
50
52
|
error_schema : Schema
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Webspicy
|
2
|
+
class Checker
|
3
|
+
class RSpecChecker
|
4
|
+
include Webspicy::Support::RSpecRunnable
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
def load_rspec_examples
|
9
|
+
config.each_scope do |scope|
|
10
|
+
scope.each_specification_file do |file, folder|
|
11
|
+
RSpec.describe file.relative_to(folder).to_s do
|
12
|
+
|
13
|
+
it 'meets the formal doc data schema' do
|
14
|
+
Webspicy.specification(file.load, file, scope)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end # class RSpecChecker
|
23
|
+
end # class Checker
|
24
|
+
end # module Webspicy
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Webspicy
|
2
|
+
module Support
|
3
|
+
module RSpecRunnable
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = Configuration.dress(config)
|
7
|
+
end
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def reset_rspec!
|
11
|
+
RSpec.reset
|
12
|
+
RSpec.configure do |c|
|
13
|
+
c.filter_gems_from_backtrace "rake"
|
14
|
+
end
|
15
|
+
load_rspec_examples
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(err=$stderr, out=$stdout)
|
19
|
+
reset_rspec!
|
20
|
+
options = RSpec::Core::ConfigurationOptions.new(config.rspec_options)
|
21
|
+
conf = RSpec::Core::Configuration.new
|
22
|
+
RSpec::Core::Runner.new(options, conf).run(err, out)
|
23
|
+
end
|
24
|
+
|
25
|
+
end # module RSpecRunnable
|
26
|
+
end # module Support
|
27
|
+
end # module Webspicy
|