webspicy 0.20.2 → 0.20.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|