webspicy 0.20.4 → 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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/bin/sandr +14 -0
  3. data/bin/search +2 -0
  4. data/bin/webspicy +2 -2
  5. data/lib/finitio/webspicy/shared.fio +10 -0
  6. data/lib/webspicy.rb +22 -53
  7. data/lib/webspicy/configuration.rb +2 -0
  8. data/lib/webspicy/configuration/scope.rb +2 -2
  9. data/lib/webspicy/configuration/single_url.rb +35 -25
  10. data/lib/webspicy/configuration/single_yml_file.rb +7 -2
  11. data/lib/webspicy/specification.rb +0 -55
  12. data/lib/webspicy/specification/service.rb +1 -5
  13. data/lib/webspicy/specification/test_case.rb +0 -49
  14. data/lib/webspicy/support/colorize.rb +1 -1
  15. data/lib/webspicy/tester.rb +1 -1
  16. data/lib/webspicy/tester/reporter/documentation.rb +4 -2
  17. data/lib/webspicy/version.rb +1 -1
  18. data/lib/webspicy/web.rb +46 -0
  19. data/lib/webspicy/{formaldoc.fio → web/formaldoc.fio} +5 -13
  20. data/lib/webspicy/web/specification.rb +68 -0
  21. data/lib/webspicy/web/specification/file_upload.rb +39 -0
  22. data/lib/webspicy/web/specification/service.rb +13 -0
  23. data/lib/webspicy/web/specification/test_case.rb +58 -0
  24. data/spec/unit/configuration/scope/test_expand_example.rb +11 -5
  25. data/spec/unit/specification/pre/test_global_request_headers.rb +3 -3
  26. data/spec/unit/specification/service/test_dress_params.rb +2 -2
  27. data/spec/unit/test_configuration.rb +1 -0
  28. data/spec/unit/web/specification/test_instantiate_url.rb +36 -0
  29. data/spec/unit/web/specification/test_url_placeholders.rb +23 -0
  30. metadata +21 -16
  31. data/lib/webspicy/specification/file_upload.rb +0 -37
  32. data/spec/blackbox/commandline.yml +0 -24
  33. data/spec/blackbox/fixtures/passing/config.rb +0 -9
  34. data/spec/blackbox/fixtures/passing/formaldef/get.yml +0 -30
  35. data/spec/unit/specification/test_instantiate_url.rb +0 -34
  36. data/spec/unit/specification/test_url_placeholders.rb +0 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9afe697a47ba4391d2e059f1bf3707c04f97f9d6fd50557104a59c6602dbd75c
4
- data.tar.gz: 66ec4036f8e76dad7af0f8b0cf83ea139a67a7b1020aa0a304c26fdc9402d593
3
+ metadata.gz: cf642b32d3e6abe3f90e53c047b8e1e973910041133ba4626d9ba7c2cb5ba333
4
+ data.tar.gz: 95860fd2e93a11f5dcecf7e175371eee6378e073ee60e22ea266c44e69265d19
5
5
  SHA512:
6
- metadata.gz: 79feb96443a47523b38f16e4cf7e7edfcd675f5a71c6b33aa452ad452aeacd5e64de1ce1ffa9ff3f5bf9cf428d59dc183e8f1455e1ce26126f2eddf9a14795fb
7
- data.tar.gz: 2761eb20d67f4c697dedf7c27496cfd36ceed4f988f1f54f17809358e570102f1e40c29c47e46f614671504dcf1c877ce2fe6ccb7a72b510994a9da95bb74de1
6
+ metadata.gz: 8415d763e56aec8f101d7140974e1ce959da7b1b0cc405c81c64976481a49b3042b442779973fe1eac76e20d9283e8aa6abb1c1363021c7c79d8f70fae59b482
7
+ data.tar.gz: 8bdfbf03a2c792fb1d772ec2260227ec24b0bfc87f4df5bf70e0baf2376818ed580f43e41d6ef1b4c753e5bc3cc620dcae1479501baea2a5cc84d65ad7370394
data/bin/sandr ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ require 'path'
3
+
4
+ def sandr(file)
5
+ file.write file.read.gsub(ARGV[0], ARGV[1])
6
+ end
7
+
8
+ Path.dir.parent.glob("lib/**/*.rb") do |file|
9
+ sandr(file)
10
+ end
11
+
12
+ Path.dir.parent.glob("spec/**/*.rb") do |file|
13
+ sandr(file)
14
+ end
data/bin/search ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/bash
2
+ grep -Rn --color $1 lib spec
data/bin/webspicy CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
  #/
3
3
  #/ Document & test web services as black-box operations.
4
- #/ (c) Enspirit SRL. Distributed under MIT licence.
4
+ #/ Webspicy vVERSION, (c) Enspirit SRL. Distributed under MIT licence.
5
5
  #/
6
6
  #/ Usage: webspicy [options] URL # run default test againt url
7
7
  #/ Usage: webspicy [options] path/to/config.rb # run whole test suite
@@ -28,7 +28,7 @@ require 'optparse'
28
28
 
29
29
  def showhelp
30
30
  file = __FILE__
31
- exec "grep ^#/<'#{file}'|cut -c4-"
31
+ exec "grep ^#/<'#{file}'|cut -c4-|sed s/VERSION/#{Webspicy::VERSION}/g"
32
32
  end
33
33
 
34
34
  ARGV.options do |opts|
@@ -0,0 +1,10 @@
1
+ @import finitio/data
2
+
3
+ Tag = String( s | s.length > 0 )
4
+
5
+ Schema =
6
+ .Finitio::System <fio> String
7
+ \( s | ::Webspicy.schema(s) )
8
+ \( s | raise "Unsupported" )
9
+
10
+ Params = .Array|.Hash
data/lib/webspicy.rb CHANGED
@@ -7,7 +7,7 @@ require 'ostruct'
7
7
  require 'yaml'
8
8
  require 'rack/test'
9
9
  require 'mustermann'
10
- require 'colorized_string'
10
+ require 'paint'
11
11
  require 'securerandom'
12
12
  require 'forwardable'
13
13
  module Webspicy
@@ -21,22 +21,10 @@ module Webspicy
21
21
  require 'webspicy/specification'
22
22
  require 'webspicy/configuration'
23
23
  require 'webspicy/tester'
24
- require 'webspicy/web'
25
24
 
26
25
  class Error < StandardError; end
27
26
  class TimeoutError < Error; end
28
27
 
29
- ###
30
- ### Backward compatibility
31
- ###
32
- Client = Tester::Client
33
- HttpClient = Web::HttpClient
34
- RackTestClient = Web::RackTestClient
35
- Resource = Specification
36
- FileUpload = Specification::FileUpload
37
- Scope = Configuration::Scope
38
- Checker = Tester::FileChecker
39
-
40
28
  ###
41
29
  ### About folders
42
30
  ###
@@ -49,10 +37,11 @@ module Webspicy
49
37
  ### About formal doc and specifications defined there
50
38
  ###
51
39
  Finitio.stdlib_path(Path.dir/"finitio")
40
+
52
41
  DEFAULT_SYSTEM = Finitio.system(<<~FIO)
53
42
  @import webspicy/scalars
54
43
  FIO
55
- FORMALDOC = Finitio.system(Path.dir/("webspicy/formaldoc.fio"))
44
+
56
45
 
57
46
  ###
58
47
  ### Exceptions that we let pass during testing
@@ -66,45 +55,6 @@ module Webspicy
66
55
  end
67
56
  module_function :default_scope
68
57
 
69
- def specification(raw, file = nil, scope = default_scope)
70
- raw = YAML.load(raw) if raw.is_a?(String)
71
- with_scope(scope) do
72
- r = FORMALDOC["Specification"].dress(raw)
73
- r.config = scope.config
74
- r.located_at!(file) if file
75
- r
76
- end
77
- rescue Finitio::Error => ex
78
- handle_finitio_error(ex, scope)
79
- end
80
- module_function :specification
81
-
82
- def service(raw, scope = default_scope)
83
- with_scope(scope) do
84
- FORMALDOC["Service"].dress(raw)
85
- end
86
- rescue Finitio::Error => ex
87
- handle_finitio_error(ex)
88
- end
89
- module_function :service
90
-
91
- def test_case(raw, scope = default_scope)
92
- with_scope(scope) do
93
- FORMALDOC["TestCase"].dress(raw)
94
- end
95
- rescue Finitio::Error => ex
96
- handle_finitio_error(ex)
97
- end
98
- module_function :test_case
99
-
100
- def handle_finitio_error(ex, scope)
101
- # msg = "#{ex.message}:\n #{ex.root_cause.message}"
102
- # msg = Support::Colorize.colorize_error(msg, scope.config)
103
- # fatal(msg)
104
- raise
105
- end
106
- module_function :handle_finitio_error
107
-
108
58
  #
109
59
  # Yields the block after having installed `scope` globally.
110
60
  #
@@ -185,4 +135,23 @@ module Webspicy
185
135
  end
186
136
  module_function :fatal
187
137
 
138
+ require 'webspicy/web'
139
+
140
+ ###
141
+ ### Backward compatibility
142
+ ###
143
+ Client = Tester::Client
144
+ HttpClient = Web::HttpClient
145
+ RackTestClient = Web::RackTestClient
146
+ Resource = Specification
147
+ FileUpload = Web::Specification::FileUpload
148
+ Scope = Configuration::Scope
149
+ Checker = Tester::FileChecker
150
+
151
+ [:specification, :service, :test_case].each do |meth|
152
+ define_method(meth) do |*args, &bl|
153
+ Webspicy::Web.send(meth, *args, &bl)
154
+ end
155
+ module_function(meth)
156
+ end
188
157
  end
@@ -39,6 +39,7 @@ module Webspicy
39
39
  :success => :green
40
40
  }
41
41
  @colorize = true
42
+ @factory = Webspicy::Web
42
43
  @scope_factory = ->(config){ Scope.new(config) }
43
44
  @client = Web::HttpClient
44
45
  @reporter = default_reporter
@@ -109,6 +110,7 @@ module Webspicy
109
110
  end
110
111
  end
111
112
 
113
+ attr_accessor :factory
112
114
  attr_accessor :scope_factory
113
115
 
114
116
  def factor_scope
@@ -35,7 +35,7 @@ module Webspicy
35
35
  def each_specification(apply_filter = true, &bl)
36
36
  return enum_for(:each_specification, apply_filter) unless block_given?
37
37
  each_specification_file(apply_filter) do |file, folder|
38
- yield Webspicy.specification(file.load, file, self)
38
+ yield config.factory.specification(file.load, file, self)
39
39
  end
40
40
  end
41
41
 
@@ -138,7 +138,7 @@ module Webspicy
138
138
  return example unless service.default_example
139
139
  h1 = service.default_example.to_info
140
140
  h2 = example.to_info
141
- ex = Specification::TestCase.new(merge_maps(h1, h2))
141
+ ex = config.factory.test_case(merge_maps(h1, h2), self)
142
142
  ex.bind(service, example.counterexample?)
143
143
  end
144
144
 
@@ -10,37 +10,47 @@ module Webspicy
10
10
  end
11
11
  attr_reader :url
12
12
 
13
- def each_specification(&bl)
13
+ def each_specification_file(*args, &bl)
14
+ return enum_for(:each_specification_file) unless block_given?
15
+ yield Path.tempfile(["specification",".yml"]).tap{|f|
16
+ f.write(specification_src)
17
+ }
18
+ end
19
+
20
+ def each_specification(*args, &bl)
14
21
  return enum_for(:each_specification) unless block_given?
15
- spec = <<~YML
22
+ yield config.factory.specification(specification_src, nil, self)
23
+ end
24
+
25
+ def specification_src
26
+ <<~YML.tap{|s| Webspicy.debug(s) }
16
27
  ---
17
- name: |-
18
- Default specification
28
+ description: |-
29
+ Getting #{url}
30
+
19
31
  url: |-
20
32
  #{url}
21
33
 
22
- services:
23
- - method: |-
24
- GET
25
- description: |-
26
- Getting #{url}
27
-
28
- input_schema: |-
29
- Any
30
- output_schema: |-
31
- Any
32
- error_schema: |-
33
- Any
34
-
35
- examples:
36
- - description: |-
37
- it returns a 200
38
- params: {}
39
- expected:
40
- status: 200
34
+ method: |-
35
+ GET
36
+
37
+ input_schema: |-
38
+ Any
39
+
40
+ output_schema: |-
41
+ Any
42
+
43
+ error_schema: |-
44
+ Any
45
+
46
+ examples:
47
+
48
+ - description: |-
49
+ it returns a 200
50
+ params: {}
51
+ expected:
52
+ status: 200
41
53
  YML
42
- Webspicy.debug(spec)
43
- yield Webspicy.specification(spec, nil, self)
44
54
  end
45
55
 
46
56
  end # class SingleUrlScope
@@ -10,9 +10,14 @@ module Webspicy
10
10
  end
11
11
  attr_reader :file
12
12
 
13
- def each_specification(&bl)
13
+ def each_specification_file(*args, &bl)
14
+ return enum_for(:each_specification_file) unless block_given?
15
+ yield(file)
16
+ end
17
+
18
+ def each_specification(*args, &bl)
14
19
  return enum_for(:each_specification) unless block_given?
15
- yield Webspicy.specification(file.read, nil, self)
20
+ yield config.factory.specification(file.read, nil, self)
16
21
  end
17
22
 
18
23
  end # class SingleYmlFileScope
@@ -10,21 +10,6 @@ module Webspicy
10
10
  attr_accessor :config
11
11
  attr_reader :location
12
12
 
13
- def self.info(raw)
14
- new(raw)
15
- end
16
-
17
- def self.singleservice(raw)
18
- converted = {
19
- name: raw[:name] || "Unamed specification",
20
- url: raw[:url],
21
- services: [
22
- Webspicy.service(raw.reject{|k| k==:url or k==:name }, Webspicy.current_scope)
23
- ]
24
- }
25
- info(converted)
26
- end
27
-
28
13
  def located_at!(location)
29
14
  @location = Path(location)
30
15
  end
@@ -39,51 +24,12 @@ module Webspicy
39
24
  @raw[:name]
40
25
  end
41
26
 
42
- def url
43
- @raw[:url]
44
- end
45
-
46
- def url_pattern
47
- @url_pattern ||= Mustermann.new(url, type: :template)
48
- end
49
-
50
27
  def services
51
28
  @raw[:services] || []
52
29
  end
53
30
 
54
- def url_placeholders
55
- url.scan(/\{([a-zA-Z]+(\.[a-zA-Z]+)*)\}/).map{|x| x.first }
56
- end
57
-
58
- def instantiate_url(params)
59
- url, rest = self.url, params.dup
60
- url_placeholders.each do |placeholder|
61
- value, rest = extract_placeholder_value(rest, placeholder)
62
- url = url.gsub("{#{placeholder}}", value.to_s)
63
- end
64
- [ url, rest ]
65
- end
66
-
67
- def to_singleservice
68
- raise NotImplementedError
69
- end
70
-
71
31
  private
72
32
 
73
- def extract_placeholder_value(params, placeholder, split = nil)
74
- return extract_placeholder_value(params, placeholder, placeholder.split(".")) unless split
75
-
76
- key = [ split.first, split.first.to_sym ].find{|k| params.has_key?(k) }
77
- raise "Missing URL parameter `#{placeholder}`" unless key
78
-
79
- if split.size == 1
80
- [ params[key], params.dup.delete_if{|k| k == key } ]
81
- else
82
- value, rest = extract_placeholder_value(params[key], placeholder, split[1..-1])
83
- [ value, params.merge(key => rest) ]
84
- end
85
- end
86
-
87
33
  def bind_services
88
34
  services.each do |s|
89
35
  s.specification = self
@@ -99,4 +45,3 @@ require_relative 'specification/post'
99
45
  require_relative 'specification/err'
100
46
  require_relative 'specification/oldies'
101
47
  require_relative 'specification/test_case'
102
- require_relative 'specification/file_upload'
@@ -21,10 +21,6 @@ module Webspicy
21
21
  specification.config
22
22
  end
23
23
 
24
- def method
25
- @raw[:method]
26
- end
27
-
28
24
  def description
29
25
  @raw[:description]
30
26
  end
@@ -68,7 +64,7 @@ module Webspicy
68
64
  def generated_counterexamples
69
65
  preconditions.map{|pre|
70
66
  pre.counterexamples(self).map{|tc|
71
- tc = Webspicy.test_case(tc, Webspicy.current_scope)
67
+ tc = config.factory.test_case(tc, Webspicy.current_scope)
72
68
  tc.bind(self, true)
73
69
  }
74
70
  }.flatten
@@ -43,10 +43,6 @@ module Webspicy
43
43
  @raw[:seeds]
44
44
  end
45
45
 
46
- def headers
47
- @raw[:headers] ||= {}
48
- end
49
-
50
46
  def metadata
51
47
  @raw[:metadata] ||= {}
52
48
  end
@@ -55,47 +51,10 @@ module Webspicy
55
51
  @raw[:tags] ||= []
56
52
  end
57
53
 
58
- def dress_params
59
- @raw.fetch(:dress_params){ true }
60
- end
61
- alias :dress_params? :dress_params
62
-
63
- def params
64
- @raw[:params] || {}
65
- end
66
-
67
- def body
68
- @raw[:body]
69
- end
70
-
71
- def file_upload
72
- @raw[:file_upload]
73
- end
74
-
75
- def located_file_upload
76
- file_upload ? file_upload.locate(specification) : nil
77
- end
78
-
79
54
  def expected
80
55
  @raw[:expected] || {}
81
56
  end
82
57
 
83
- def expected_content_type
84
- expected[:content_type]
85
- end
86
-
87
- def expected_status
88
- expected[:status]
89
- end
90
-
91
- def is_expected_status?(status)
92
- expected_status === status
93
- end
94
-
95
- def has_expected_status?
96
- not expected[:status].nil?
97
- end
98
-
99
58
  def expected_error
100
59
  expected[:error]
101
60
  end
@@ -104,14 +63,6 @@ module Webspicy
104
63
  !expected_error.nil?
105
64
  end
106
65
 
107
- def expected_headers
108
- expected[:headers] || {}
109
- end
110
-
111
- def has_expected_headers?
112
- !expected_headers.empty?
113
- end
114
-
115
66
  def assert
116
67
  @raw[:assert] || []
117
68
  end
@@ -5,7 +5,7 @@ module Webspicy
5
5
  def colorize(str, kind, config = nil)
6
6
  return str if config && !config.colorize
7
7
  color = (config || self.config).colors[kind]
8
- ColorizedString[str].colorize(color)
8
+ Paint[str, color]
9
9
  end
10
10
  module_function :colorize
11
11
 
@@ -98,7 +98,7 @@ module Webspicy
98
98
  def load_specification(spec_file)
99
99
  @spec_file = spec_file
100
100
  reporter.before_spec_file
101
- Webspicy.specification(spec_file.load, spec_file, scope)
101
+ config.factory.specification(spec_file.load, spec_file, scope)
102
102
  rescue *PASSTHROUGH_EXCEPTIONS
103
103
  raise
104
104
  rescue Exception => e
@@ -7,8 +7,10 @@ module Webspicy
7
7
  INDENT = " ".freeze
8
8
 
9
9
  def spec_file_line(spec_file)
10
- relative_path = Path(spec_file).relative_to(config.folder)
11
- colorize_section(">> #{relative_path}", config)
10
+ path = Path(spec_file).expand_path
11
+ path = path.relative_to(config.folder)
12
+ path = spec_file if path.to_s.start_with?(".")
13
+ colorize_section(">> #{path}", config)
12
14
  end
13
15
 
14
16
  def spec_file_error_line(spec_file, ex)
@@ -2,7 +2,7 @@ module Webspicy
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 20
5
- TINY = 4
5
+ TINY = 5
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
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,13 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ class Service < Webspicy::Specification::Service
5
+
6
+ def method
7
+ @raw[:method]
8
+ end
9
+
10
+ end # class Service
11
+ end # class Specification
12
+ end # module Web
13
+ 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
- subject{ Scope.new({}).send(:expand_example, service, example) }
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,36 @@
1
+ require "spec_helper"
2
+ module Webspicy
3
+ module Web
4
+ describe Specification, "instantiate_url" do
5
+
6
+ it 'does nothing when the url has no placeholder' do
7
+ r = Specification.new(url: "/test/a/url")
8
+ url, params = r.instantiate_url(foo: "bar")
9
+ expect(url).to eq("/test/a/url")
10
+ expect(params).to eq(foo: "bar")
11
+ end
12
+
13
+ it 'instantiates placeholders and strips corresponding params' do
14
+ r = Specification.new(url: "/test/{foo}/url")
15
+ url, params = r.instantiate_url(foo: "bar", baz: "coz")
16
+ expect(url).to eq("/test/bar/url")
17
+ expect(params).to eq(baz: "coz")
18
+ end
19
+
20
+ it 'instantiates placeholders and strips corresponding params even when multiple' do
21
+ r = Specification.new(url: "/test/{foo}/url/{bar}")
22
+ url, params = r.instantiate_url(foo: "bar", bar: "baz", baz: "coz")
23
+ expect(url).to eq("/test/bar/url/baz")
24
+ expect(params).to eq(baz: "coz")
25
+ end
26
+
27
+ it 'supports placeholders corresponding to subentities' do
28
+ r = Specification.new(url: "/test/{foo.id}/url")
29
+ url, params = r.instantiate_url(foo: {id: "bar"}, baz: "coz")
30
+ expect(url).to eq("/test/bar/url")
31
+ expect(params).to eq(foo: {}, baz: "coz")
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+ module Webspicy
3
+ module Web
4
+ describe Specification, "url_placeholders" do
5
+
6
+ it 'returns an empty array on none' do
7
+ r = Specification.new(url: "/test/a/url")
8
+ expect(r.url_placeholders).to eq([])
9
+ end
10
+
11
+ it 'returns all placeholders' do
12
+ r = Specification.new(url: "/test/{foo}/url/{bar}")
13
+ expect(r.url_placeholders).to eq(["foo", "bar"])
14
+ end
15
+
16
+ it 'returns all placeholders expr' do
17
+ r = Specification.new(url: "/test/{foo.id}/url/{bar}")
18
+ expect(r.url_placeholders).to eq(["foo.id", "bar"])
19
+ end
20
+
21
+ end
22
+ end
23
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webspicy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.20.4
4
+ version: 0.20.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-13 00:00:00.000000000 Z
11
+ date: 2021-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -171,19 +171,19 @@ dependencies:
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  - !ruby/object:Gem::Dependency
174
- name: colorize
174
+ name: paint
175
175
  requirement: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - "~>"
178
178
  - !ruby/object:Gem::Version
179
- version: 0.8.1
179
+ version: '2.2'
180
180
  type: :runtime
181
181
  prerelease: false
182
182
  version_requirements: !ruby/object:Gem::Requirement
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 0.8.1
186
+ version: '2.2'
187
187
  - !ruby/object:Gem::Dependency
188
188
  name: openapi3_parser
189
189
  requirement: !ruby/object:Gem::Requirement
@@ -201,6 +201,8 @@ dependencies:
201
201
  description: Webspicy helps testing web services as software operation black boxes
202
202
  email: blambeau@gmail.com
203
203
  executables:
204
+ - sandr
205
+ - search
204
206
  - webspicy
205
207
  extensions: []
206
208
  extra_rdoc_files: []
@@ -208,22 +210,23 @@ files:
208
210
  - Gemfile
209
211
  - README.md
210
212
  - Rakefile
213
+ - bin/sandr
214
+ - bin/search
211
215
  - bin/webspicy
212
216
  - doc/1-black-box-scene.md
213
217
  - doc/2-black-box-testing.md
214
218
  - doc/3-specification-importance.md
215
219
  - doc/4-sequence-diagram.md
216
220
  - lib/finitio/webspicy/scalars.fio
221
+ - lib/finitio/webspicy/shared.fio
217
222
  - lib/webspicy.rb
218
223
  - lib/webspicy/configuration.rb
219
224
  - lib/webspicy/configuration/scope.rb
220
225
  - lib/webspicy/configuration/single_url.rb
221
226
  - lib/webspicy/configuration/single_yml_file.rb
222
- - lib/webspicy/formaldoc.fio
223
227
  - lib/webspicy/specification.rb
224
228
  - lib/webspicy/specification/condition.rb
225
229
  - lib/webspicy/specification/err.rb
226
- - lib/webspicy/specification/file_upload.rb
227
230
  - lib/webspicy/specification/oldies.rb
228
231
  - lib/webspicy/specification/oldies/bridge.rb
229
232
  - lib/webspicy/specification/oldies/errcondition.rb
@@ -279,14 +282,16 @@ files:
279
282
  - lib/webspicy/web/client/http_client.rb
280
283
  - lib/webspicy/web/client/rack_test_client.rb
281
284
  - lib/webspicy/web/client/support.rb
285
+ - lib/webspicy/web/formaldoc.fio
282
286
  - lib/webspicy/web/invocation.rb
283
287
  - lib/webspicy/web/mocker.rb
284
288
  - lib/webspicy/web/mocker/config.ru
285
289
  - lib/webspicy/web/openapi.rb
286
290
  - lib/webspicy/web/openapi/generator.rb
287
- - spec/blackbox/commandline.yml
288
- - spec/blackbox/fixtures/passing/config.rb
289
- - spec/blackbox/fixtures/passing/formaldef/get.yml
291
+ - lib/webspicy/web/specification.rb
292
+ - lib/webspicy/web/specification/file_upload.rb
293
+ - lib/webspicy/web/specification/service.rb
294
+ - lib/webspicy/web/specification/test_case.rb
290
295
  - spec/spec_helper.rb
291
296
  - spec/unit/configuration/config.rb
292
297
  - spec/unit/configuration/scope/test_each_service.rb
@@ -297,8 +302,6 @@ files:
297
302
  - spec/unit/specification/service/test_dress_params.rb
298
303
  - spec/unit/specification/test_case/test_mutate.rb
299
304
  - spec/unit/specification/test_condition.rb
300
- - spec/unit/specification/test_instantiate_url.rb
301
- - spec/unit/specification/test_url_placeholders.rb
302
305
  - spec/unit/support/hooks/test_fire_after_each.rb
303
306
  - spec/unit/support/hooks/test_fire_around.rb
304
307
  - spec/unit/support/hooks/test_fire_before_each.rb
@@ -314,13 +317,15 @@ files:
314
317
  - spec/unit/tester/test_assertions.rb
315
318
  - spec/unit/web/mocker/test_mocker.rb
316
319
  - spec/unit/web/openapi/test_generator.rb
320
+ - spec/unit/web/specification/test_instantiate_url.rb
321
+ - spec/unit/web/specification/test_url_placeholders.rb
317
322
  - tasks/gem.rake
318
323
  - tasks/test.rake
319
324
  homepage: http://github.com/enspirit/webspicy
320
325
  licenses:
321
326
  - MIT
322
327
  metadata: {}
323
- post_install_message:
328
+ post_install_message:
324
329
  rdoc_options: []
325
330
  require_paths:
326
331
  - lib
@@ -335,8 +340,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
335
340
  - !ruby/object:Gem::Version
336
341
  version: '0'
337
342
  requirements: []
338
- rubygems_version: 3.1.4
339
- signing_key:
343
+ rubygems_version: 3.2.1
344
+ signing_key:
340
345
  specification_version: 4
341
346
  summary: Webspicy helps testing web services as software operation black boxes!
342
347
  test_files: []
@@ -1,37 +0,0 @@
1
- module Webspicy
2
- class Specification
3
- class FileUpload
4
-
5
- def initialize(raw)
6
- @path = raw[:path]
7
- @content_type = raw[:content_type]
8
- @param_name = raw[:param_name] || "file"
9
- end
10
-
11
- attr_reader :path, :content_type, :param_name
12
-
13
- def self.info(raw)
14
- new(raw)
15
- end
16
-
17
- def locate(specification)
18
- FileUpload.new({
19
- path: specification.locate(path),
20
- content_type: content_type
21
- })
22
- end
23
-
24
- def to_info
25
- { path: path.to_s,
26
- content_type: content_type,
27
- param_name: param_name }
28
- end
29
-
30
- def to_s
31
- "FileUpload(#{to_info})"
32
- end
33
- alias :inspect :to_s
34
-
35
- end # class FileUpload
36
- end # class Specification
37
- end # module Webspicy
@@ -1,24 +0,0 @@
1
- ---
2
- command:
3
- webspicy {options} {args}
4
-
5
- examples:
6
-
7
- - description: |-
8
- when called on a passing path
9
- args:
10
- - ./fixtures/passing/config.rb
11
- assert:
12
- exit_code:
13
- 0
14
- stdout: |-
15
- GET /, when requested
16
- v It has a 200 response status
17
- v It has a `Content-Type: application/json` response header
18
- v Its output meets the expected data schema
19
- v Assert notEmpty
20
- v Assert pathFD('', :hello => "World" )
21
-
22
-
23
- 1 spec file, 1 example, 0 counterexample
24
- 5 assertions, 0 error, 0 failure
@@ -1,9 +0,0 @@
1
- Webspicy::Configuration.new(Path.dir) do |c|
2
- app = ->(env){
3
- [ 200,
4
- {"Content-Type" => "application/json"},
5
- [ {hello: "World"}.to_json ]
6
- ]
7
- }
8
- c.client = Webspicy::Tester::RackTestClient.for(app)
9
- end
@@ -1,30 +0,0 @@
1
- ---
2
- url: |-
3
- /
4
-
5
- method: |-
6
- GET
7
-
8
- description: |-
9
- Returns hello world
10
-
11
- input_schema: |-
12
- { }
13
-
14
- output_schema: |-
15
- { hello: String }
16
-
17
- error_schema: |-
18
- .
19
-
20
- examples:
21
-
22
- - description: |-
23
- when requested
24
- params: {}
25
- expected:
26
- content_type: application/json
27
- status: 200
28
- assert:
29
- - notEmpty
30
- - pathFD('', :hello => "World" )
@@ -1,34 +0,0 @@
1
- require "spec_helper"
2
- module Webspicy
3
- describe Specification, "instantiate_url" do
4
-
5
- it 'does nothing when the url has no placeholder' do
6
- r = Specification.new(url: "/test/a/url")
7
- url, params = r.instantiate_url(foo: "bar")
8
- expect(url).to eq("/test/a/url")
9
- expect(params).to eq(foo: "bar")
10
- end
11
-
12
- it 'instantiates placeholders and strips corresponding params' do
13
- r = Specification.new(url: "/test/{foo}/url")
14
- url, params = r.instantiate_url(foo: "bar", baz: "coz")
15
- expect(url).to eq("/test/bar/url")
16
- expect(params).to eq(baz: "coz")
17
- end
18
-
19
- it 'instantiates placeholders and strips corresponding params even when multiple' do
20
- r = Specification.new(url: "/test/{foo}/url/{bar}")
21
- url, params = r.instantiate_url(foo: "bar", bar: "baz", baz: "coz")
22
- expect(url).to eq("/test/bar/url/baz")
23
- expect(params).to eq(baz: "coz")
24
- end
25
-
26
- it 'supports placeholders corresponding to subentities' do
27
- r = Specification.new(url: "/test/{foo.id}/url")
28
- url, params = r.instantiate_url(foo: {id: "bar"}, baz: "coz")
29
- expect(url).to eq("/test/bar/url")
30
- expect(params).to eq(foo: {}, baz: "coz")
31
- end
32
-
33
- end
34
- end
@@ -1,21 +0,0 @@
1
- require "spec_helper"
2
- module Webspicy
3
- describe Specification, "url_placeholders" do
4
-
5
- it 'returns an empty array on none' do
6
- r = Specification.new(url: "/test/a/url")
7
- expect(r.url_placeholders).to eq([])
8
- end
9
-
10
- it 'returns all placeholders' do
11
- r = Specification.new(url: "/test/{foo}/url/{bar}")
12
- expect(r.url_placeholders).to eq(["foo", "bar"])
13
- end
14
-
15
- it 'returns all placeholders expr' do
16
- r = Specification.new(url: "/test/{foo.id}/url/{bar}")
17
- expect(r.url_placeholders).to eq(["foo.id", "bar"])
18
- end
19
-
20
- end
21
- end