webspicy 0.24.0 → 0.26.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e101e5e0888e85dfb80ab43b4577c9f01ce793c04b0e974dcf12f6225743881a
4
- data.tar.gz: a42aa4e7212a25bac508d4cdf2ed8707d2c9f9cb01df8a9ef2dd6646e6b15206
3
+ metadata.gz: 49f65b0370d22d9ff81cb309ec916a292c7c852e25d783b11b72de21d5466772
4
+ data.tar.gz: be4edc16dd8809028b243687b69da34591fec591c520ad72f9a5786a8f324130
5
5
  SHA512:
6
- metadata.gz: 230a0309efbaef05b78adf7f4ad7f8c5c1caf189be13fa5fba18ffa700e4b1da91659a267905f784e435279848405e639544e8072c9f8b748a577b13bde11aee
7
- data.tar.gz: a465e061e7f8aaed5942ad097e243e4a0c18c19e51b88f5805c3e18310d4dcdf70677e8178239368a91f6753c7f33b3530f49383655080a1248d9dbe5f6f1c47
6
+ metadata.gz: 89f2301b02f83bfe3e48286854e6c51c3b55dfd588d2dc1c4f1f4f343a8cde91eccbf592d3c7516975143f743869aa26719b2d5389b318b95284c5f9239a3761
7
+ data.tar.gz: f63b1e52e4ae1edebe0b2e9f6aaa7360a4208f8b9fe7bc917ef19a6710d28b003bad13bcea31874935528cf428444e7548d6bc6fac42d46526fc11b84a327dba
@@ -15,5 +15,3 @@ module Webspicy
15
15
  end # module Pre
16
16
  end # class Specification
17
17
  end # module Webspicy
18
- require_relative 'pre/global_request_headers'
19
- require_relative 'pre/robust_to_invalid_input'
@@ -108,6 +108,18 @@ module Webspicy
108
108
  end
109
109
  end
110
110
 
111
+ def eq(path, expected = NO_ARG)
112
+ path, expected = '', path if expected == NO_ARG
113
+ target = @assertions.extract_path(@target, path)
114
+ Predicate.eq(target, expected).assert!
115
+ end
116
+
117
+ def eql(path, expected = NO_ARG)
118
+ path, expected = '', path if expected == NO_ARG
119
+ target = @assertions.extract_path(@target, path)
120
+ Predicate.eq(target, expected).assert!
121
+ end
122
+
111
123
  private
112
124
 
113
125
  def DateTime(str)
@@ -90,6 +90,16 @@ module Webspicy
90
90
  !match(target, path, rx)
91
91
  end
92
92
 
93
+ def eq(target, path, expected)
94
+ target = extract_path(target, path)
95
+ target == expected
96
+ end
97
+
98
+ def eql(target, path, expected)
99
+ target = extract_path(target, path)
100
+ value_equal(target, expected)
101
+ end
102
+
93
103
  public
94
104
 
95
105
  def extract_path(target, path = NO_ARG)
@@ -1,7 +1,7 @@
1
1
  module Webspicy
2
2
  module Version
3
3
  MAJOR = 0
4
- MINOR = 24
4
+ MINOR = 26
5
5
  TINY = 0
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
@@ -0,0 +1,38 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Post
5
+ class ETagCachingProtocol
6
+ include Webspicy::Specification::Post
7
+
8
+ MATCH = /It supports the ETag\/If-None-Match caching protocol/
9
+
10
+ def self.match(service, descr)
11
+ return nil unless descr =~ MATCH
12
+ ETagCachingProtocol.new
13
+ end
14
+
15
+ def check!
16
+ res = invocation.response
17
+ etag = res.headers['ETag']
18
+ fail!("No ETag response header found") unless etag
19
+
20
+ url, _ = test_case.specification.instantiate_url(test_case.params)
21
+ url = scope.to_real_url(url, test_case){|u,_| u }
22
+
23
+ response = client.api.get(url, {}, test_case.headers.merge({
24
+ 'If-None-Match' => etag
25
+ }))
26
+ fail!("304 expected") unless response.status == 304
27
+
28
+ response = client.api.get(url, {}, test_case.headers.merge({
29
+ 'If-None-Match' => "W/somethingelse"
30
+ }))
31
+ fail!("2xx expected") if response.status == 304
32
+ end
33
+
34
+ end # class ETagCachingProtocol
35
+ end # module Post
36
+ end # module Webspicy
37
+ end # module Web
38
+ end # class Specification
@@ -0,0 +1,41 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Post
5
+ class LastModifiedCachingProtocol
6
+ include Webspicy::Specification::Post
7
+
8
+ MATCH = /It supports the Last-Modified\/If-Modified-Since caching protocol/
9
+
10
+ def self.match(service, descr)
11
+ return nil unless descr =~ MATCH
12
+ LastModifiedCachingProtocol.new
13
+ end
14
+
15
+ def check!
16
+ res = invocation.response
17
+ last_modified = res.headers['Last-Modified']
18
+ fail!("No last-modified response header found") unless last_modified
19
+
20
+ # check it fits the HTTP-date format or fail
21
+ Time.httpdate(last_modified) rescue fail!("Not valid Last-Modified response header")
22
+
23
+ url, _ = test_case.specification.instantiate_url(test_case.params)
24
+ url = scope.to_real_url(url, test_case){|u,_| u }
25
+
26
+ response = client.api.get(url, {}, test_case.headers.merge({
27
+ 'If-Modified-Since' => last_modified
28
+ }))
29
+ fail!("304 expected") unless response.status == 304
30
+
31
+ response = client.api.get(url, {}, test_case.headers.merge({
32
+ 'If-Modified-Since' => "Thu, 08 Jun 1970 19:06:27 GMT"
33
+ }))
34
+ fail!("2xx expected") if response.status == 304
35
+ end
36
+
37
+ end # class LastModifiedCachingProtocol
38
+ end # module Post
39
+ end # module Webspicy
40
+ end # module Web
41
+ end # class Specification
@@ -0,0 +1,67 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Post
5
+ class SemanticsPreservedByRefactoring
6
+ include ::Webspicy::Specification::Post
7
+
8
+ MATCH = /The data output semantics is preserved by the refactoring/
9
+
10
+ def self.match(service, descr)
11
+ return nil unless descr =~ MATCH
12
+ SemanticsPreservedByRefactoring.new
13
+ end
14
+
15
+ def instrument
16
+ end
17
+
18
+ def check!
19
+ test_id = {
20
+ description: test_case.description,
21
+ seeds: test_case.seeds,
22
+ url: test_case.service.specification.url,
23
+ method: test_case.service.method,
24
+ params: test_case.params,
25
+ headers: test_case.headers.reject{|k| k == 'Authorization' },
26
+ metadata: test_case.metadata,
27
+ }
28
+ sha1 = Digest::SHA1.hexdigest(test_id.to_json)
29
+
30
+ record_file_path = config.folder/".morpheus/#{sha1}.key.json"
31
+ record_file_path.parent.mkdir_p
32
+ record_file_path.write(JSON.pretty_generate(test_id))
33
+
34
+ response = invocation.response
35
+ test_data = {
36
+ status: response.status,
37
+ headers: response.headers,
38
+ body: JSON.parse(response.body),
39
+ }
40
+
41
+ case ENV['MORPHEUS'].upcase
42
+ when 'RECORD'
43
+ expected_file_path = config.folder/".morpheus/#{sha1}.expected.json"
44
+ expected_file_path.write(JSON.pretty_generate(test_data))
45
+ when 'CHECK'
46
+ expected_file_path = config.folder/".morpheus/#{sha1}.expected.json"
47
+ expected = expected_file_path.load
48
+
49
+ actual_file_path = config.folder/".morpheus/#{sha1}.actual.json"
50
+ actual_file_path.write(JSON.pretty_generate(test_data))
51
+ actual = actual_file_path.load
52
+
53
+ fail!("Semantics has changed.") unless values_equal?(actual, expected)
54
+ end
55
+ end
56
+
57
+ private
58
+
59
+ def values_equal?(a, b)
60
+ Tester::Asserter.new(a).eql('', b)
61
+ end
62
+
63
+ end # SemanticsPreservedByRefactoring
64
+ end # module Post
65
+ end # module Webspicy
66
+ end # module Web
67
+ end # class Specification
@@ -0,0 +1,9 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Post
5
+ include Webspicy::Specification::Post
6
+ end # module Post
7
+ end # class Specification
8
+ end # module Web
9
+ end # module Webspicy
@@ -0,0 +1,38 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Pre
5
+ class GlobalRequestHeaders
6
+ include Pre
7
+
8
+ DEFAULT_OPTIONS = {}
9
+
10
+ def initialize(headers, options = {}, &bl)
11
+ @headers = headers
12
+ @options = DEFAULT_OPTIONS.merge(options)
13
+ @matcher = bl
14
+ end
15
+ attr_reader :headers, :matcher
16
+
17
+ def match(service, pre)
18
+ if matcher
19
+ return self if matcher.call(service)
20
+ nil
21
+ else
22
+ self
23
+ end
24
+ end
25
+
26
+ def instrument
27
+ extra = headers.reject{|k|
28
+ test_case.headers.has_key?(k)
29
+ }
30
+ puts "Instrumenting #{test_case.object_id}"
31
+ test_case.headers.merge!(extra)
32
+ end
33
+
34
+ end # class GlobalRequestHeaders
35
+ end # module Pre
36
+ end # class Specification
37
+ end # module Web
38
+ end # module Webspicy
@@ -0,0 +1,70 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Pre
5
+ class RobustToInvalidInput
6
+ include Pre
7
+
8
+ def self.match(service, pre)
9
+ self.new
10
+ end
11
+
12
+ def match(service, pre)
13
+ self
14
+ end
15
+
16
+ def counterexamples(service)
17
+ spec = service.specification
18
+ first = service.examples.first
19
+ cexamples = []
20
+ cexamples += url_randomness_counterexamples(service, first) if first
21
+ cexamples += empty_input_counterexamples(service, first) if first
22
+ cexamples
23
+ end
24
+
25
+ protected
26
+
27
+ def url_randomness_counterexamples(service, first)
28
+ service.specification.url_placeholders.map{|p|
29
+ first.mutate({
30
+ :description => "it is robust to URL randomness on param `#{p}` (RobustToInvalidInput)",
31
+ :dress_params => false,
32
+ :params => first.params.merge(p => (SecureRandom.random_number * 100000000).to_i),
33
+ :expected => {
34
+ status: Support::StatusRange.str("4xx")
35
+ },
36
+ :assert => []
37
+ })
38
+ }
39
+ end
40
+
41
+ def empty_input_counterexamples(service, first)
42
+ placeholders = service.specification.url_placeholders
43
+ empty_input = first.params.reject{|k| !placeholders.include?(k) }
44
+ if invalid_input?(service, empty_input)
45
+ [first.mutate({
46
+ :description => "it is robust to an invalid empty input (RobustToInvalidInput)",
47
+ :dress_params => false,
48
+ :params => empty_input,
49
+ :expected => {
50
+ status: Support::StatusRange.str("4xx")
51
+ },
52
+ :assert => []
53
+ })]
54
+ else
55
+ []
56
+ end
57
+ end
58
+
59
+ def invalid_input?(service, empty_input)
60
+ service.input_schema.dress(empty_input)
61
+ false
62
+ rescue Finitio::Error
63
+ true
64
+ end
65
+
66
+ end # class RobustToInvalidInput
67
+ end # module Pre
68
+ end # class Specification
69
+ end # module Web
70
+ end # module Webspicy
@@ -0,0 +1,9 @@
1
+ module Webspicy
2
+ module Web
3
+ class Specification
4
+ module Pre
5
+ include Webspicy::Specification::Pre
6
+ end # module Pre
7
+ end # class Specification
8
+ end # module Web
9
+ end # module Webspicy
@@ -63,6 +63,8 @@ module Webspicy
63
63
  end # class Specification
64
64
  end # module Web
65
65
  end # module Webspicy
66
+ require_relative 'specification/pre'
67
+ require_relative 'specification/post'
66
68
  require_relative 'specification/service'
67
69
  require_relative 'specification/test_case'
68
70
  require_relative 'specification/file_upload'
data/lib/webspicy.rb CHANGED
@@ -10,6 +10,7 @@ require 'mustermann'
10
10
  require 'paint'
11
11
  require 'securerandom'
12
12
  require 'forwardable'
13
+ require 'predicate'
13
14
  module Webspicy
14
15
 
15
16
  ###
@@ -3,92 +3,99 @@ require 'spec_helper'
3
3
  module Webspicy
4
4
  class Tester
5
5
  describe Assertions do
6
- include Assertions
7
6
 
8
- public :extract_path
7
+ class A
8
+ include Assertions
9
+
10
+ public :extract_path
11
+ end
12
+
13
+ let(:a) do
14
+ A.new
15
+ end
9
16
 
10
17
  it 'has an extract_path helper' do
11
18
  target = { foo: "Hello", bar: { foo: "Hello" }, baz: [{ foo: "world" }] }
12
- expect(extract_path(target)).to be(target)
13
- expect(extract_path(target, nil)).to be(target)
14
- expect(extract_path(target, '')).to be(target)
15
- expect(extract_path(target, 'foo')).to eql("Hello")
16
- expect(extract_path(target, 'bar/foo')).to eql("Hello")
17
- expect(extract_path(target, 'baz/0')).to eql({ foo: "world" })
18
- expect(extract_path(target, 'baz/0/foo')).to eql("world")
19
+ expect(a.extract_path(target)).to be(target)
20
+ expect(a.extract_path(target, nil)).to be(target)
21
+ expect(a.extract_path(target, '')).to be(target)
22
+ expect(a.extract_path(target, 'foo')).to eql("Hello")
23
+ expect(a.extract_path(target, 'bar/foo')).to eql("Hello")
24
+ expect(a.extract_path(target, 'baz/0')).to eql({ foo: "world" })
25
+ expect(a.extract_path(target, 'baz/0/foo')).to eql("world")
19
26
  end
20
27
 
21
28
  it 'has an includes() assertion' do
22
- expect(includes [], 1).to be(false)
23
- expect(includes [5, 1], 1).to be(true)
29
+ expect(a.includes [], 1).to be(false)
30
+ expect(a.includes [5, 1], 1).to be(true)
24
31
  end
25
32
 
26
33
  it 'has a notIncludes() assertion' do
27
- expect(notIncludes [], 1).to be(true)
28
- expect(notIncludes [5, 1], 1).to be(false)
34
+ expect(a.notIncludes [], 1).to be(true)
35
+ expect(a.notIncludes [5, 1], 1).to be(false)
29
36
  end
30
37
 
31
38
  it 'has an exists() assertion' do
32
- expect(exists nil).to be(false)
33
- expect(exists []).to be(true)
34
- expect(exists [1]).to be(true)
35
- expect(exists({ foo: [] }, 'foo')).to be(true)
36
- expect(exists({ foo: {} }, 'foo')).to be(true)
37
- expect(exists({ foo: {} }, 'foo/bar')).to be(false)
39
+ expect(a.exists nil).to be(false)
40
+ expect(a.exists []).to be(true)
41
+ expect(a.exists [1]).to be(true)
42
+ expect(a.exists({ foo: [] }, 'foo')).to be(true)
43
+ expect(a.exists({ foo: {} }, 'foo')).to be(true)
44
+ expect(a.exists({ foo: {} }, 'foo/bar')).to be(false)
38
45
  end
39
46
 
40
47
  it 'has a notExists() assertion' do
41
- expect(notExists nil).to be(true)
42
- expect(notExists []).to be(false)
43
- expect(notExists [1]).to be(false)
44
- expect(notExists({ foo: [] }, 'foo')).to be(false)
45
- expect(notExists({ foo: {} }, 'foo')).to be(false)
46
- expect(notExists({ foo: {} }, 'foo/bar')).to be(true)
48
+ expect(a.notExists nil).to be(true)
49
+ expect(a.notExists []).to be(false)
50
+ expect(a.notExists [1]).to be(false)
51
+ expect(a.notExists({ foo: [] }, 'foo')).to be(false)
52
+ expect(a.notExists({ foo: {} }, 'foo')).to be(false)
53
+ expect(a.notExists({ foo: {} }, 'foo/bar')).to be(true)
47
54
  end
48
55
 
49
56
  it 'has an empty() assertion' do
50
- expect(empty []).to be(true)
51
- expect(empty [1]).to be(false)
52
- expect(empty({ foo: [] }, 'foo')).to be(true)
53
- expect(empty({ foo: [1] }, 'foo')).to be(false)
57
+ expect(a.empty []).to be(true)
58
+ expect(a.empty [1]).to be(false)
59
+ expect(a.empty({ foo: [] }, 'foo')).to be(true)
60
+ expect(a.empty({ foo: [1] }, 'foo')).to be(false)
54
61
  end
55
62
 
56
63
  it 'has a notEmpty() assertion' do
57
- expect(notEmpty []).to be(false)
58
- expect(notEmpty [1]).to be(true)
59
- expect(notEmpty({ foo: [] }, 'foo')).to be(false)
60
- expect(notEmpty({ foo: [1] }, 'foo')).to be(true)
64
+ expect(a.notEmpty []).to be(false)
65
+ expect(a.notEmpty [1]).to be(true)
66
+ expect(a.notEmpty({ foo: [] }, 'foo')).to be(false)
67
+ expect(a.notEmpty({ foo: [1] }, 'foo')).to be(true)
61
68
  end
62
69
 
63
70
  it 'has a size() assertion' do
64
- expect(size [], 0).to be(true)
65
- expect(size [], 1).to be(false)
66
- expect(size [12], 1).to be(true)
67
- expect(size({ foo: [] }, 'foo', 0)).to be(true)
68
- expect(size({ foo: [] }, 'foo', 1)).to be(false)
69
- expect(size({ foo: ['bar'] }, 'foo', 1)).to be(true)
71
+ expect(a.size [], 0).to be(true)
72
+ expect(a.size [], 1).to be(false)
73
+ expect(a.size [12], 1).to be(true)
74
+ expect(a.size({ foo: [] }, 'foo', 0)).to be(true)
75
+ expect(a.size({ foo: [] }, 'foo', 1)).to be(false)
76
+ expect(a.size({ foo: ['bar'] }, 'foo', 1)).to be(true)
70
77
  end
71
78
 
72
79
  it 'has an idIn assertion' do
73
- expect(idIn [{id: 1}, {id: 2}], [1, 2]).to be(true)
74
- expect(idIn [{id: 1}, {id: 2}], [2, 1]).to be(true)
75
- expect(idIn [{id: 1}, {id: 2}], [1, 3]).to be(false)
76
- expect(idIn [{id: 1}, {id: 2}], [1]).to be(false)
77
- expect(idIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1, 2])).to be(true)
78
-
79
- expect(idIn({id: 1}, [1])).to be(true)
80
- expect(idIn({id: 1}, [2])).to be(false)
80
+ expect(a.idIn [{id: 1}, {id: 2}], [1, 2]).to be(true)
81
+ expect(a.idIn [{id: 1}, {id: 2}], [2, 1]).to be(true)
82
+ expect(a.idIn [{id: 1}, {id: 2}], [1, 3]).to be(false)
83
+ expect(a.idIn [{id: 1}, {id: 2}], [1]).to be(false)
84
+ expect(a.idIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1, 2])).to be(true)
85
+
86
+ expect(a.idIn({id: 1}, [1])).to be(true)
87
+ expect(a.idIn({id: 1}, [2])).to be(false)
81
88
  end
82
89
 
83
90
  it 'has an idNotIn assertion' do
84
- expect(idNotIn [{id: 1}, {id: 2}], [3]).to be(true)
85
- expect(idNotIn [{id: 1}, {id: 2}], [3, 4]).to be(true)
86
- expect(idNotIn [{id: 1}, {id: 2}], [1]).to be(false)
87
- expect(idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1])).to be(false)
88
- expect(idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [3])).to be(true)
89
-
90
- expect(idNotIn({id: 1}, [3])).to be(true)
91
- expect(idNotIn({id: 1}, [1])).to be(false)
91
+ expect(a.idNotIn [{id: 1}, {id: 2}], [3]).to be(true)
92
+ expect(a.idNotIn [{id: 1}, {id: 2}], [3, 4]).to be(true)
93
+ expect(a.idNotIn [{id: 1}, {id: 2}], [1]).to be(false)
94
+ expect(a.idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [1])).to be(false)
95
+ expect(a.idNotIn({ foo: [{id: 1}, {id: 2}] }, 'foo', [3])).to be(true)
96
+
97
+ expect(a.idNotIn({id: 1}, [3])).to be(true)
98
+ expect(a.idNotIn({id: 1}, [1])).to be(false)
92
99
  end
93
100
 
94
101
  it 'has an idFD assertion' do
@@ -96,35 +103,35 @@ module Webspicy
96
103
  { id: 1, bar: "bar" },
97
104
  { id: 2, bar: "baz" }
98
105
  ] }
99
- element = element_with_id(target, 'foo', 1)
100
- expect(idFD(element, bar: "bar")).to be(true)
101
- expect(idFD(element, bar: "baz")).to be(false)
102
- expect(idFD(element, baz: "boz")).to be(false)
106
+ element = a.element_with_id(target, 'foo', 1)
107
+ expect(a.idFD(element, bar: "bar")).to be(true)
108
+ expect(a.idFD(element, bar: "baz")).to be(false)
109
+ expect(a.idFD(element, baz: "boz")).to be(false)
103
110
 
104
111
  target = { foo: { id: 1, bar: "bar" } }
105
- element = element_with_id(target, 'foo', 1)
106
- expect(idFD(element, bar: "bar")).to be(true)
107
- expect(idFD(element, bar: "baz")).to be(false)
108
- expect(idFD(element, baz: "boz")).to be(false)
112
+ element = a.element_with_id(target, 'foo', 1)
113
+ expect(a.idFD(element, bar: "bar")).to be(true)
114
+ expect(a.idFD(element, bar: "baz")).to be(false)
115
+ expect(a.idFD(element, baz: "boz")).to be(false)
109
116
  end
110
117
 
111
118
  it 'has a pathFD assertion' do
112
119
  target = { foo: { bar: "baz"} }
113
- expect(pathFD(target, 'foo', bar: "baz")).to be(true)
114
- expect(pathFD(target, 'foo', bar: "boz")).to be(false)
115
- expect(pathFD(target, 'foo', boz: "biz")).to be(false)
120
+ expect(a.pathFD(target, 'foo', bar: "baz")).to be(true)
121
+ expect(a.pathFD(target, 'foo', bar: "boz")).to be(false)
122
+ expect(a.pathFD(target, 'foo', boz: "biz")).to be(false)
116
123
  end
117
124
 
118
125
  it 'has a match assertion' do
119
126
  target = "hello world"
120
- expect(match(target, '', /world/)).to be(true)
121
- expect(match(target, '', /foobar/)).to be(false)
127
+ expect(a.match(target, '', /world/)).to be(true)
128
+ expect(a.match(target, '', /foobar/)).to be(false)
122
129
  end
123
130
 
124
131
  it 'has a notMatch assertion' do
125
132
  target = "hello world"
126
- expect(notMatch(target, '', /world/)).to be(false)
127
- expect(notMatch(target, '', /foobar/)).to be(true)
133
+ expect(a.notMatch(target, '', /world/)).to be(false)
134
+ expect(a.notMatch(target, '', /foobar/)).to be(true)
128
135
  end
129
136
 
130
137
  end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'webspicy/web/specification/pre/global_request_headers'
3
+
4
+ module Webspicy
5
+ module Web
6
+ class Specification
7
+ module Pre
8
+ describe GlobalRequestHeaders do
9
+ let(:gbr){
10
+ GlobalRequestHeaders.new('Accept' => 'application/json')
11
+ }
12
+
13
+ def instrument(tc)
14
+ t = OpenStruct.new(test_case: tc)
15
+ gbr.bind(t).instrument
16
+ end
17
+
18
+ describe "instrument" do
19
+ it 'injects the headers' do
20
+ tc = Web::Specification::TestCase.new({})
21
+ instrument(tc)
22
+ expect(tc.headers['Accept']).to eql("application/json")
23
+ end
24
+
25
+ it 'keeps original headers unchanged' do
26
+ tc = Web::Specification::TestCase.new({
27
+ headers: {
28
+ 'Content-Type' => 'text/plain'
29
+ }
30
+ })
31
+ instrument(tc)
32
+ expect(tc.headers['Content-Type']).to eql("text/plain")
33
+ expect(tc.headers['Accept']).to eql("application/json")
34
+ end
35
+
36
+ it 'has low precedence' do
37
+ tc = Web::Specification::TestCase.new({
38
+ headers: {
39
+ 'Accept' => 'text/plain'
40
+ }
41
+ })
42
+ instrument(tc)
43
+ expect(tc.headers['Accept']).to eql("text/plain")
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ 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.24.0
4
+ version: 0.26.0
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: 2023-05-26 00:00:00.000000000 Z
11
+ date: 2023-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -28,9 +28,9 @@ dependencies:
28
28
  name: sinatra
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - ">"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.2.4
33
+ version: '3.0'
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '4.0'
@@ -38,9 +38,9 @@ dependencies:
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
41
- - - ">="
41
+ - - ">"
42
42
  - !ruby/object:Gem::Version
43
- version: 2.2.4
43
+ version: '3.0'
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '4.0'
@@ -62,30 +62,42 @@ dependencies:
62
62
  name: rspec_junit_formatter
63
63
  requirement: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - "~>"
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0.6'
68
+ - - "<"
66
69
  - !ruby/object:Gem::Version
67
- version: 0.4.1
70
+ version: '0.7'
68
71
  type: :development
69
72
  prerelease: false
70
73
  version_requirements: !ruby/object:Gem::Requirement
71
74
  requirements:
72
- - - "~>"
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0.6'
78
+ - - "<"
73
79
  - !ruby/object:Gem::Version
74
- version: 0.4.1
80
+ version: '0.7'
75
81
  - !ruby/object:Gem::Dependency
76
82
  name: rack-test
77
83
  requirement: !ruby/object:Gem::Requirement
78
84
  requirements:
79
- - - "~>"
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '2.0'
88
+ - - "<"
80
89
  - !ruby/object:Gem::Version
81
- version: 0.6.3
90
+ version: '3.0'
82
91
  type: :runtime
83
92
  prerelease: false
84
93
  version_requirements: !ruby/object:Gem::Requirement
85
94
  requirements:
86
- - - "~>"
95
+ - - ">="
87
96
  - !ruby/object:Gem::Version
88
- version: 0.6.3
97
+ version: '2.0'
98
+ - - "<"
99
+ - !ruby/object:Gem::Version
100
+ version: '3.0'
89
101
  - !ruby/object:Gem::Dependency
90
102
  name: finitio
91
103
  requirement: !ruby/object:Gem::Requirement
@@ -110,16 +122,22 @@ dependencies:
110
122
  name: http
111
123
  requirement: !ruby/object:Gem::Requirement
112
124
  requirements:
113
- - - "~>"
125
+ - - ">="
114
126
  - !ruby/object:Gem::Version
115
- version: 4.4.1
127
+ version: '5.0'
128
+ - - "<"
129
+ - !ruby/object:Gem::Version
130
+ version: '6.0'
116
131
  type: :runtime
117
132
  prerelease: false
118
133
  version_requirements: !ruby/object:Gem::Requirement
119
134
  requirements:
120
- - - "~>"
135
+ - - ">="
121
136
  - !ruby/object:Gem::Version
122
- version: 4.4.1
137
+ version: '5.0'
138
+ - - "<"
139
+ - !ruby/object:Gem::Version
140
+ version: '6.0'
123
141
  - !ruby/object:Gem::Dependency
124
142
  name: path
125
143
  requirement: !ruby/object:Gem::Requirement
@@ -138,22 +156,22 @@ dependencies:
138
156
  name: rack-robustness
139
157
  requirement: !ruby/object:Gem::Requirement
140
158
  requirements:
141
- - - "~>"
142
- - !ruby/object:Gem::Version
143
- version: '1.1'
144
159
  - - ">="
145
160
  - !ruby/object:Gem::Version
146
- version: 1.1.0
161
+ version: '1.2'
162
+ - - "<"
163
+ - !ruby/object:Gem::Version
164
+ version: '2.0'
147
165
  type: :runtime
148
166
  prerelease: false
149
167
  version_requirements: !ruby/object:Gem::Requirement
150
168
  requirements:
151
- - - "~>"
152
- - !ruby/object:Gem::Version
153
- version: '1.1'
154
169
  - - ">="
155
170
  - !ruby/object:Gem::Version
156
- version: 1.1.0
171
+ version: '1.2'
172
+ - - "<"
173
+ - !ruby/object:Gem::Version
174
+ version: '2.0'
157
175
  - !ruby/object:Gem::Dependency
158
176
  name: mustermann
159
177
  requirement: !ruby/object:Gem::Requirement
@@ -200,16 +218,22 @@ dependencies:
200
218
  name: openapi3_parser
201
219
  requirement: !ruby/object:Gem::Requirement
202
220
  requirements:
203
- - - "~>"
221
+ - - ">="
222
+ - !ruby/object:Gem::Version
223
+ version: '0.9'
224
+ - - "<"
204
225
  - !ruby/object:Gem::Version
205
- version: 0.8.2
226
+ version: '0.10'
206
227
  type: :runtime
207
228
  prerelease: false
208
229
  version_requirements: !ruby/object:Gem::Requirement
209
230
  requirements:
210
- - - "~>"
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: '0.9'
234
+ - - "<"
211
235
  - !ruby/object:Gem::Version
212
- version: 0.8.2
236
+ version: '0.10'
213
237
  - !ruby/object:Gem::Dependency
214
238
  name: mustache
215
239
  requirement: !ruby/object:Gem::Requirement
@@ -266,6 +290,26 @@ dependencies:
266
290
  - - "~>"
267
291
  - !ruby/object:Gem::Version
268
292
  version: '3.7'
293
+ - !ruby/object:Gem::Dependency
294
+ name: predicate
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - ">="
298
+ - !ruby/object:Gem::Version
299
+ version: '2.8'
300
+ - - "<"
301
+ - !ruby/object:Gem::Version
302
+ version: '3.0'
303
+ type: :runtime
304
+ prerelease: false
305
+ version_requirements: !ruby/object:Gem::Requirement
306
+ requirements:
307
+ - - ">="
308
+ - !ruby/object:Gem::Version
309
+ version: '2.8'
310
+ - - "<"
311
+ - !ruby/object:Gem::Version
312
+ version: '3.0'
269
313
  description: Webspicy helps testing web services as software operation black boxes
270
314
  email: blambeau@gmail.com
271
315
  executables:
@@ -300,8 +344,6 @@ files:
300
344
  - lib/webspicy/specification/post/missing_condition_impl.rb
301
345
  - lib/webspicy/specification/post/unexpected_condition_impl.rb
302
346
  - lib/webspicy/specification/pre.rb
303
- - lib/webspicy/specification/pre/global_request_headers.rb
304
- - lib/webspicy/specification/pre/robust_to_invalid_input.rb
305
347
  - lib/webspicy/specification/service.rb
306
348
  - lib/webspicy/specification/test_case.rb
307
349
  - lib/webspicy/support.rb
@@ -361,6 +403,13 @@ files:
361
403
  - lib/webspicy/web/openapi/generator.rb
362
404
  - lib/webspicy/web/specification.rb
363
405
  - lib/webspicy/web/specification/file_upload.rb
406
+ - lib/webspicy/web/specification/post.rb
407
+ - lib/webspicy/web/specification/post/etag_caching_protocol.rb
408
+ - lib/webspicy/web/specification/post/last_modified_caching_protocol.rb
409
+ - lib/webspicy/web/specification/post/semantics_preserved_by_refactoring.rb
410
+ - lib/webspicy/web/specification/pre.rb
411
+ - lib/webspicy/web/specification/pre/global_request_headers.rb
412
+ - lib/webspicy/web/specification/pre/robust_to_invalid_input.rb
364
413
  - lib/webspicy/web/specification/service.rb
365
414
  - lib/webspicy/web/specification/test_case.rb
366
415
  - spec/spec_helper.rb
@@ -369,7 +418,6 @@ files:
369
418
  - spec/unit/configuration/scope/test_each_specification.rb
370
419
  - spec/unit/configuration/scope/test_expand_example.rb
371
420
  - spec/unit/configuration/scope/test_to_real_url.rb
372
- - spec/unit/specification/pre/test_global_request_headers.rb
373
421
  - spec/unit/specification/service/test_dress_params.rb
374
422
  - spec/unit/specification/test_case/test_mutate.rb
375
423
  - spec/unit/specification/test_condition.rb
@@ -392,6 +440,7 @@ files:
392
440
  - spec/unit/web/inferer/test_inferer.rb
393
441
  - spec/unit/web/mocker/test_mocker.rb
394
442
  - spec/unit/web/openapi/test_generator.rb
443
+ - spec/unit/web/specification/pre/test_global_request_headers.rb
395
444
  - spec/unit/web/specification/test_instantiate_url.rb
396
445
  - spec/unit/web/specification/test_url_placeholders.rb
397
446
  - tasks/gem.rake
@@ -400,7 +449,7 @@ homepage: http://github.com/enspirit/webspicy
400
449
  licenses:
401
450
  - MIT
402
451
  metadata: {}
403
- post_install_message:
452
+ post_install_message:
404
453
  rdoc_options: []
405
454
  require_paths:
406
455
  - lib
@@ -416,7 +465,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
416
465
  version: '0'
417
466
  requirements: []
418
467
  rubygems_version: 3.3.26
419
- signing_key:
468
+ signing_key:
420
469
  specification_version: 4
421
470
  summary: Webspicy helps testing web services as software operation black boxes!
422
471
  test_files: []
@@ -1,35 +0,0 @@
1
- module Webspicy
2
- class Specification
3
- module Pre
4
- class GlobalRequestHeaders
5
- include Pre
6
-
7
- DEFAULT_OPTIONS = {}
8
-
9
- def initialize(headers, options = {}, &bl)
10
- @headers = headers
11
- @options = DEFAULT_OPTIONS.merge(options)
12
- @matcher = bl
13
- end
14
- attr_reader :headers, :matcher
15
-
16
- def match(service, pre)
17
- if matcher
18
- return self if matcher.call(service)
19
- nil
20
- else
21
- self
22
- end
23
- end
24
-
25
- def instrument
26
- extra = headers.reject{|k|
27
- test_case.headers.has_key?(k)
28
- }
29
- test_case.headers.merge!(extra)
30
- end
31
-
32
- end # class GlobalRequestHeaders
33
- end # module Pre
34
- end # class Specification
35
- end # module Webspicy
@@ -1,68 +0,0 @@
1
- module Webspicy
2
- class Specification
3
- module Pre
4
- class RobustToInvalidInput
5
- include Pre
6
-
7
- def self.match(service, pre)
8
- self.new
9
- end
10
-
11
- def match(service, pre)
12
- self
13
- end
14
-
15
- def counterexamples(service)
16
- spec = service.specification
17
- first = service.examples.first
18
- cexamples = []
19
- cexamples += url_randomness_counterexamples(service, first) if first
20
- cexamples += empty_input_counterexamples(service, first) if first
21
- cexamples
22
- end
23
-
24
- protected
25
-
26
- def url_randomness_counterexamples(service, first)
27
- service.specification.url_placeholders.map{|p|
28
- first.mutate({
29
- :description => "it is robust to URL randomness on param `#{p}` (RobustToInvalidInput)",
30
- :dress_params => false,
31
- :params => first.params.merge(p => (SecureRandom.random_number * 100000000).to_i),
32
- :expected => {
33
- status: Support::StatusRange.str("4xx")
34
- },
35
- :assert => []
36
- })
37
- }
38
- end
39
-
40
- def empty_input_counterexamples(service, first)
41
- placeholders = service.specification.url_placeholders
42
- empty_input = first.params.reject{|k| !placeholders.include?(k) }
43
- if invalid_input?(service, empty_input)
44
- [first.mutate({
45
- :description => "it is robust to an invalid empty input (RobustToInvalidInput)",
46
- :dress_params => false,
47
- :params => empty_input,
48
- :expected => {
49
- status: Support::StatusRange.str("4xx")
50
- },
51
- :assert => []
52
- })]
53
- else
54
- []
55
- end
56
- end
57
-
58
- def invalid_input?(service, empty_input)
59
- service.input_schema.dress(empty_input)
60
- false
61
- rescue Finitio::Error
62
- true
63
- end
64
-
65
- end # class RobustToInvalidInput
66
- end # module Pre
67
- end # class Specification
68
- end # module Webspicy
@@ -1,47 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Webspicy
4
- class Specification
5
- module Pre
6
- describe GlobalRequestHeaders do
7
- let(:gbr){
8
- GlobalRequestHeaders.new('Accept' => 'application/json')
9
- }
10
-
11
- def instrument(tc)
12
- t = OpenStruct.new(test_case: tc)
13
- gbr.bind(t).instrument
14
- end
15
-
16
- describe "instrument" do
17
- it 'injects the headers' do
18
- tc = Web::Specification::TestCase.new({})
19
- instrument(tc)
20
- expect(tc.headers['Accept']).to eql("application/json")
21
- end
22
-
23
- it 'keeps original headers unchanged' do
24
- tc = Web::Specification::TestCase.new({
25
- headers: {
26
- 'Content-Type' => 'text/plain'
27
- }
28
- })
29
- instrument(tc)
30
- expect(tc.headers['Content-Type']).to eql("text/plain")
31
- expect(tc.headers['Accept']).to eql("application/json")
32
- end
33
-
34
- it 'has low precedence' do
35
- tc = Web::Specification::TestCase.new({
36
- headers: {
37
- 'Accept' => 'text/plain'
38
- }
39
- })
40
- instrument(tc)
41
- expect(tc.headers['Accept']).to eql("text/plain")
42
- end
43
- end
44
- end
45
- end
46
- end
47
- end