webspicy 0.20.2 → 0.20.7
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/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/post/missing_condition_impl.rb +2 -2
- data/lib/webspicy/specification/post/unexpected_condition_impl.rb +2 -2
- data/lib/webspicy/specification/service.rb +1 -5
- data/lib/webspicy/specification/test_case.rb +0 -49
- data/lib/webspicy/support/colorize.rb +7 -1
- data/lib/webspicy/tester.rb +15 -19
- data/lib/webspicy/tester/fakesmtp.rb +1 -1
- data/lib/webspicy/tester/fakesmtp/email.rb +13 -0
- data/lib/webspicy/tester/reporter.rb +5 -0
- data/lib/webspicy/tester/reporter/documentation.rb +30 -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/errcondition_met.rb +1 -3
- 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/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/tester/fakesmtp/test_email.rb +93 -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 +15 -10
- 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
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$/ )
|
@@ -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
|
}
|
@@ -4,7 +4,7 @@ module Webspicy
|
|
4
4
|
describe Service, "dress_params" do
|
5
5
|
|
6
6
|
it 'symbolizes keys' do
|
7
|
-
service = Webspicy.service({
|
7
|
+
service = Webspicy::Web.service({
|
8
8
|
method: "GET",
|
9
9
|
description: "Test service",
|
10
10
|
preconditions: "Foo",
|
@@ -17,7 +17,7 @@ module Webspicy
|
|
17
17
|
end
|
18
18
|
|
19
19
|
it 'supports an array' do
|
20
|
-
service = Webspicy.service({
|
20
|
+
service = Webspicy::Web.service({
|
21
21
|
method: "GET",
|
22
22
|
description: "Test service",
|
23
23
|
preconditions: "Foo",
|
@@ -36,6 +36,7 @@ module Webspicy
|
|
36
36
|
expect(c).to be_a(Configuration)
|
37
37
|
expect(c.folder).to eq(Path.pwd)
|
38
38
|
expect(c.each_scope.to_a.size).to eql(1)
|
39
|
+
expect(c.each_scope.to_a.first.each_specification_file.to_a.size).to eql(1)
|
39
40
|
expect(c.each_scope.to_a.first.each_specification.to_a.size).to eql(1)
|
40
41
|
end
|
41
42
|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'webspicy/tester/fakesmtp'
|
3
|
+
module Webspicy
|
4
|
+
class Tester
|
5
|
+
class Fakesmtp
|
6
|
+
describe Email do
|
7
|
+
|
8
|
+
DATA = JSON.parse <<~J
|
9
|
+
{
|
10
|
+
"attachments": [],
|
11
|
+
"headerLines": [
|
12
|
+
{
|
13
|
+
"key": "date",
|
14
|
+
"line": "Date: Tue, 20 Apr 2021 14:06:13 +0000"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
"key": "from",
|
18
|
+
"line": "From: info@mydomain.be"
|
19
|
+
},
|
20
|
+
{
|
21
|
+
"key": "reply-to",
|
22
|
+
"line": "Reply-To: test@email.be"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"key": "to",
|
26
|
+
"line": "To: support@mydomain.fr"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"key": "message-id",
|
30
|
+
"line": "Message-ID: <607edfd56836e_1b0492af@1d3356d02030.mail>"
|
31
|
+
},
|
32
|
+
{
|
33
|
+
"key": "subject",
|
34
|
+
"line": "Subject: Hello World"
|
35
|
+
},
|
36
|
+
{
|
37
|
+
"key": "mime-version",
|
38
|
+
"line": "Mime-Version: 1.0"
|
39
|
+
}
|
40
|
+
],
|
41
|
+
"html": "<p>Hello World!!</p>",
|
42
|
+
"text": "Hello World!!",
|
43
|
+
"textAsHtml": "Hello World!!",
|
44
|
+
"subject": "Hello World",
|
45
|
+
"date": "2021-04-20T14:06:13.000Z",
|
46
|
+
"to": {
|
47
|
+
"value": [
|
48
|
+
{
|
49
|
+
"address": "support@mydomain.fr",
|
50
|
+
"name": ""
|
51
|
+
}
|
52
|
+
],
|
53
|
+
"html": "<span class=\\"mp_address_group\\"><a href=\\"mailto:support@mydomain.fr\\" class=\\"mp_address_email\\">support@mydomain.fr</a></span>",
|
54
|
+
"text": "support@mydomain.fr"
|
55
|
+
},
|
56
|
+
"from": {
|
57
|
+
"value": [
|
58
|
+
{
|
59
|
+
"address": "info@mydomain.be",
|
60
|
+
"name": ""
|
61
|
+
}
|
62
|
+
],
|
63
|
+
"html": "<span class=\\"mp_address_group\\"><a href=\\"mailto:info@mydomain.be\\" class=\\"mp_address_email\\">info@mydomain.be</a></span>",
|
64
|
+
"text": "info@mydomain.be"
|
65
|
+
},
|
66
|
+
"messageId": "<607edfd56836e_1b0492af@1d3356d02030.mail>",
|
67
|
+
"replyTo": {
|
68
|
+
"value": [
|
69
|
+
{
|
70
|
+
"address": "test@email.be",
|
71
|
+
"name": ""
|
72
|
+
}
|
73
|
+
],
|
74
|
+
"html": "<span class=\\"mp_address_group\\"><a href=\\"mailto:test@email.be\\" class=\\"mp_address_email\\">test@email.be</a></span>",
|
75
|
+
"text": "test@email.be"
|
76
|
+
}
|
77
|
+
}
|
78
|
+
J
|
79
|
+
|
80
|
+
subject{
|
81
|
+
Email.new(DATA)
|
82
|
+
}
|
83
|
+
|
84
|
+
it 'works as expected' do
|
85
|
+
expect(subject.from).to eql("info@mydomain.be")
|
86
|
+
expect(subject.to).to eql(["support@mydomain.fr"])
|
87
|
+
expect(subject.subject).to eql("Hello World")
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|