webspicy 0.24.0 → 0.26.0

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