tshield 0.11.19.0 → 0.13.2.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
- SHA256:
3
- metadata.gz: d7be1450b970496e9bc17514f42fcda00e98cf8e0b5e269b54b809f38e752653
4
- data.tar.gz: fd139f9746a3cd6470aec409b809f0d62b6af0915ca0db910a64a7470ad6f44b
2
+ SHA1:
3
+ metadata.gz: 87b26d114e278df9af5848487fbe41a5783b69e9
4
+ data.tar.gz: 36fd3d713a4c30d82631d7141c83154c90659281
5
5
  SHA512:
6
- metadata.gz: c9727017f7c245277e64ab4bb4eca0a82f08eaf8ceb0c4b4424693f42dc2267eb0420ea15b557adadba4a69eb3116e7fca60717686a641b8602e77a4d99f183e
7
- data.tar.gz: c4d633f5d08a2801de7e30e56a2a947b64d3e91db13ca803689a2f05d15053812eeb82585f7058e6a0a2ee9a72170b9d0f5f44557c751f7ce77800073aa23f2e
6
+ metadata.gz: 5bd5f43cc13647026b199ad55ab149b842d6bbedae8dcfccb9a345dcc99abb72c132641ede6cd80bff36bd7e5e9123deb8dde882c4573b2d441a8c9c3e430df9
7
+ data.tar.gz: 956d0f30dcf1c57ba98e3488deb0d8d80367c8c5ffa6c5b6c383eed282bf17f679463df5403e93ba17033ca1dcb8bfc69afe3deeff778a44b9431b47089c7f47
data/README.md CHANGED
@@ -3,9 +3,10 @@ TShield
3
3
 
4
4
  [![Build Status](https://travis-ci.org/diegorubin/tshield.svg)](https://travis-ci.org/diegorubin/tshield)
5
5
  [![Coverage Status](https://coveralls.io/repos/github/diegorubin/tshield/badge.svg?branch=master)](https://coveralls.io/github/diegorubin/tshield?branch=master)
6
- [![SourceLevel](https://app.sourcelevel.io/github/diegorubin/tshield.svg)](https://app.sourcelevel.io/github/diegorubin/tshield)
6
+ [![SourceLevel](https://app.sourcelevel.io/github/diegorubin/-/tshield.svg)](https://app.sourcelevel.io/github/diegorubin/-/tshield)
7
7
  [![Join the chat at https://gitter.im/diegorubin/tshield](https://badges.gitter.im/diegorubin/tshield.svg)](https://gitter.im/diegorubin/tshield?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8
8
  [![Gem Version](https://badge.fury.io/rb/tshield.svg)](https://badge.fury.io/rb/tshield)
9
+ ![TShield Publish](https://github.com/diegorubin/tshield/workflows/TShield%20Publish/badge.svg)
9
10
 
10
11
  ## API mocks for development and testing
11
12
  TShield is an open source proxy for mocks API responses.
@@ -68,6 +69,17 @@ domains:
68
69
  - /users
69
70
  ```
70
71
 
72
+ **Windows Compatibility:** If you need to use Tshield in Windows SO, change the config file and set the windows_compatibility to true.
73
+
74
+ Eg:
75
+ ```yaml
76
+ windows_compatibility: true
77
+ request:
78
+ # wait time for real service
79
+ timeout: 8
80
+ ...
81
+ ```
82
+
71
83
  ## Config options for Pattern Matching
72
84
 
73
85
  An example of file to create a stub:
@@ -93,6 +105,10 @@ Optional request attributes:
93
105
  this stub will be returned if all headers are in request.
94
106
  * **query**: works like headers but use query params.
95
107
 
108
+ Optional response attributes:
109
+
110
+ * **delay**: integer that represents time in seconds that the response will be delayed to return
111
+
96
112
  **Important**: If VCR config conflicts with Matching config Matching will be
97
113
  used. Matching config have priority.
98
114
 
@@ -155,6 +171,7 @@ To register stub into a session create an object with following attributes:
155
171
  "method": "GET",
156
172
  "path": "/matching/example",
157
173
  "response": {
174
+ "delay": 5,
158
175
  "body": "matching-example-response-in-session",
159
176
  "headers": {},
160
177
  "status": 200
@@ -195,6 +212,8 @@ domains:
195
212
  - transfer-encoding
196
213
  paths:
197
214
  - /secure
215
+ delay:
216
+ '/secure': 10
198
217
 
199
218
  'http://localhost:9092':
200
219
  name: 'my-other-service'
@@ -205,6 +224,8 @@ domains:
205
224
  - transfer-encoding
206
225
  paths:
207
226
  - /users
227
+ delay:
228
+ '/users': 5
208
229
  ```
209
230
  **request**
210
231
  * **timeout**: wait time for real service in seconds
@@ -220,6 +241,7 @@ domains:
220
241
  * **filters**: Implementation of before or after filters used in domain requests
221
242
  * **excluded_headers**: <<some_description>>
222
243
  * **paths**: Paths list of all services that will be called. Used to filter what domain will "receive the request"
244
+ * **delay**: List of times in seconds that the response will be delayed to return for an specific path defined above
223
245
 
224
246
  ## Manage Sessions
225
247
 
@@ -250,6 +272,16 @@ _DELETE_ to http://localhost:4567/sessions
250
272
  curl -X DELETE \
251
273
  http://localhost:4567/sessions
252
274
  ```
275
+ ### Append secondary TShield session
276
+ **Append session. Secondary sessions will used only for read content in VCR mode, writes will be do in the main session. Append only works if exists a current session setted.**
277
+
278
+ _POST_ to http://localhost:4567/sessions?name=<<same_name>>
279
+
280
+ ```
281
+ curl -X POST \
282
+ 'http://localhost:4567/sessions/append?name=my_valid'
283
+ ```
284
+
253
285
  ## [Experimental] Config options for gRPC
254
286
 
255
287
  ```yaml
@@ -14,5 +14,6 @@ domains:
14
14
  skip_query_params:
15
15
  - b
16
16
  paths:
17
+ - /fake
17
18
  - /users
18
19
  - /resources
@@ -32,6 +32,7 @@ module TShield
32
32
  attr_reader :domains
33
33
  attr_reader :tcp_servers
34
34
  attr_reader :session_path
35
+ attr_reader :windows_compatibility
35
36
 
36
37
  def initialize(attributes)
37
38
  attributes.each { |key, value| instance_variable_set("@#{key}", value) }
@@ -103,6 +104,10 @@ module TShield
103
104
  session_path || '/sessions'
104
105
  end
105
106
 
107
+ def get_questionmark_char
108
+ windows_compatibility ? '%3f' : '?'
109
+ end
110
+
106
111
  def grpc
107
112
  defaults = { 'port' => 5678, 'proto_dir' => 'proto', 'services' => {} }
108
113
  defaults.merge(@grpc || {})
@@ -126,5 +131,10 @@ module TShield
126
131
  )
127
132
  raise 'Startup aborted'
128
133
  end
134
+
135
+ def get_delay(domain, path)
136
+ ((domains[domain] || {})['delay'] || {})[path] || 0
137
+ end
138
+
129
139
  end
130
140
  end
@@ -10,6 +10,11 @@ module TShield
10
10
  session[:name] if session
11
11
  end
12
12
 
13
+ def self.secondary_sessions(request)
14
+ session = TShield::Sessions.current(request.ip)
15
+ session[:secondary_sessions] if session
16
+ end
17
+
13
18
  def self.current_session_call(request, callid, method)
14
19
  session = TShield::Sessions.current(request.ip)
15
20
  session ? session[:counter].current(callid, method) : 0
@@ -43,6 +43,7 @@ module TShield
43
43
  request_content_type = request.content_type
44
44
 
45
45
  session_name = TShield::Controllers::Helpers::SessionHelpers.current_session_name(request)
46
+ secondary_sessions = TShield::Controllers::Helpers::SessionHelpers.secondary_sessions(request)
46
47
  session_call = TShield::Controllers::Helpers::SessionHelpers
47
48
  .current_session_call(request, callid, method)
48
49
 
@@ -51,6 +52,7 @@ module TShield
51
52
  headers: Helpers.build_headers(request),
52
53
  raw_query: request.env['QUERY_STRING'],
53
54
  session: session_name,
55
+ secondary_sessions: secondary_sessions,
54
56
  call: session_call,
55
57
  ip: request.ip
56
58
  }
@@ -67,12 +69,12 @@ module TShield
67
69
  unless api_response
68
70
  add_headers(options, path)
69
71
 
70
- api_response ||= TShield::RequestVCR.new(path, options.clone).response
72
+ api_response ||= TShield::RequestVCR.new(path, options.clone).vcr_response
71
73
  api_response.headers.reject! do |key, _v|
72
74
  configuration.get_excluded_headers(domain(path)).include?(key)
73
75
  end
74
76
  end
75
-
77
+
76
78
  logger.info(
77
79
  "original=#{api_response.original} method=#{method} path=#{path} "\
78
80
  "content-type=#{request_content_type} "\
@@ -80,6 +82,7 @@ module TShield
80
82
  )
81
83
  TShield::Controllers::Helpers::SessionHelpers.update_session_call(request, callid, method)
82
84
 
85
+ delay(path)
83
86
  status api_response.status
84
87
  headers api_response.headers
85
88
  body api_response.body
@@ -98,6 +101,13 @@ module TShield
98
101
  def domain(path)
99
102
  @domain ||= configuration.get_domain_for(path)
100
103
  end
104
+
105
+ def delay(path)
106
+ delay_in_seconds = configuration.get_delay(domain(path), path) || 0
107
+ logger.info("Response with delay of #{delay_in_seconds} seconds")
108
+ sleep delay_in_seconds
109
+ end
110
+
101
111
  end
102
112
  end
103
113
  end
@@ -23,6 +23,9 @@ module TShield
23
23
  end
24
24
 
25
25
  def self.register_post(app, session_path)
26
+ app.post "#{session_path}/append" do
27
+ TShield::Sessions.append(request.ip, params[:name]).to_json
28
+ end
26
29
  app.post session_path do
27
30
  TShield::Sessions.start(request.ip, params[:name]).to_json
28
31
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AppendSessionWithoutMainSessionError < RuntimeError
4
+ end
@@ -16,6 +16,7 @@ module TShield
16
16
  module_name = options['module']
17
17
 
18
18
  path = create_destiny(module_name, method_name, request)
19
+ save_request(path, request)
19
20
  response = saved_response(path)
20
21
  if response
21
22
  TShield.logger.info("returning saved response for request #{request.to_json} saved into #{hexdigest(request)}")
@@ -26,7 +27,7 @@ module TShield
26
27
  client_class = Object.const_get("#{module_name}::Stub")
27
28
  client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
28
29
  response = client_instance.send(method_name, request)
29
- save_request_and_response(path, request, response)
30
+ save_response(path, response)
30
31
  response
31
32
  end
32
33
 
@@ -39,11 +40,6 @@ module TShield
39
40
  Kernel.const_get(response_class).new(content)
40
41
  end
41
42
 
42
- def save_request_and_response(path, request, response)
43
- save_request(path, request)
44
- save_response(path, response)
45
- end
46
-
47
43
  def save_request(path, request)
48
44
  file = File.open(File.join(path, 'original_request'), 'w')
49
45
  file.puts request.to_json
@@ -8,6 +8,14 @@ module TShield
8
8
  result = filter_stubs(stubs[@options[:session]] || {})
9
9
  return result if result
10
10
 
11
+ find_in_secondary_sessions(stubs, @options[:secondary_sessions] || [])
12
+ end
13
+
14
+ def find_in_secondary_sessions(stubs, sessions)
15
+ sessions.each do |session|
16
+ result = filter_stubs(stubs[session] || {})
17
+ return result if result
18
+ end
11
19
  filter_stubs(stubs[DEFAULT_SESSION] || {}) unless @options[:session] == DEFAULT_SESSION
12
20
  end
13
21
 
@@ -13,8 +13,8 @@ module TShield
13
13
 
14
14
  protected
15
15
 
16
- def session_destiny(request_path)
17
- session = @options[:session]
16
+ def session_destiny(request_path, current_session = nil)
17
+ session = current_session || @options[:session]
18
18
  return request_path unless session
19
19
 
20
20
  request_path = File.join(request_path, session)
@@ -22,19 +22,19 @@ module TShield
22
22
  request_path
23
23
  end
24
24
 
25
- def content_destiny
26
- "#{destiny}.content"
25
+ def content_destiny(current_session = nil)
26
+ "#{destiny(current_session)}.content"
27
27
  end
28
28
 
29
- def headers_destiny
30
- "#{destiny}.json"
29
+ def headers_destiny(current_session = nil)
30
+ "#{destiny(current_session)}.json"
31
31
  end
32
32
 
33
- def destiny
33
+ def destiny(current_session = nil)
34
34
  request_path = File.join('requests')
35
35
  Dir.mkdir(request_path) unless File.exist?(request_path)
36
36
 
37
- request_path = session_destiny(request_path)
37
+ request_path = session_destiny(request_path, current_session)
38
38
 
39
39
  name_path = File.join(request_path, name)
40
40
  Dir.mkdir(name_path) unless File.exist?(name_path)
@@ -29,6 +29,7 @@ module TShield
29
29
 
30
30
  @matched = current_response
31
31
 
32
+ sleep matched['delay'] || 0
32
33
  TShield::Response.new(self.class.read_body(matched['body']),
33
34
  matched['headers'],
34
35
  matched['status'])
@@ -2,11 +2,11 @@
2
2
 
3
3
  require 'httparty'
4
4
  require 'json'
5
- require 'byebug'
6
5
 
7
6
  require 'digest/sha1'
8
7
 
9
8
  require 'tshield/configuration'
9
+ require 'tshield/logger'
10
10
  require 'tshield/options'
11
11
  require 'tshield/request'
12
12
  require 'tshield/response'
@@ -14,6 +14,8 @@ require 'tshield/response'
14
14
  module TShield
15
15
  # Module to write and read saved responses
16
16
  class RequestVCR < TShield::Request
17
+ attr_reader :vcr_response
18
+
17
19
  def initialize(path, options = {})
18
20
  super()
19
21
  @path = path
@@ -35,27 +37,31 @@ module TShield
35
37
  _method, @url, @options = filter.new.filter(method, @url, @options)
36
38
  end
37
39
 
38
- if exists
39
- response.original = false
40
- resp = response
40
+ in_session = find_in_sessions
41
+ if in_session
42
+ # TODO: create concept of global session in vcr
43
+ in_session = nil if in_session == 'global'
44
+ @vcr_response = response(in_session)
45
+ @vcr_response.original = false
41
46
  else
47
+ TShield.logger.info("calling original service for request with options #{@options}")
42
48
  raw = HTTParty.send(method.to_s, @url, @options)
43
49
 
44
50
  original_response = save(raw)
45
51
  original_response.original = true
46
- resp = original_response
52
+ @vcr_response = original_response
47
53
  end
48
54
 
49
55
  configuration.get_after_filters(domain).each do |filter|
50
- resp = filter.new.filter(resp)
56
+ @vcr_response = filter.new.filter(@vcr_response)
51
57
  end
52
- resp
53
58
  end
54
59
 
55
- def response
56
- @response ||= TShield::Response.new(saved_content['body'],
57
- saved_content['headers'] || [],
58
- saved_content['status'] || 200)
60
+ def response(session)
61
+ response_content = saved_content(session)
62
+ TShield::Response.new(response_content['body'],
63
+ response_content['headers'] || [],
64
+ response_content['status'] || 200)
59
65
  end
60
66
 
61
67
  private
@@ -72,12 +78,31 @@ module TShield
72
78
  @method ||= @options[:method].downcase
73
79
  end
74
80
 
81
+ def apply_set_cookie_header_values(raw_response, headers = {})
82
+
83
+ headers_clone = headers.clone
84
+
85
+ field = raw_response.get_fields('Set-Cookie')
86
+
87
+ if !field.nil? && !field.empty?
88
+ cookies_values = []
89
+ field.each { |value| cookies_values.push(value) }
90
+ headers_clone['Set-Cookie'] = cookies_values
91
+ end
92
+
93
+ headers_clone
94
+ end
95
+
75
96
  def save(raw_response)
76
97
  headers = {}
98
+
77
99
  raw_response.headers.each do |k, v|
100
+ next if k == 'set-cookie'
78
101
  headers[k] = v unless configuration.not_save_headers(domain).include? k
79
102
  end
80
103
 
104
+ headers = apply_set_cookie_header_values(raw_response, headers)
105
+
81
106
  content = {
82
107
  body: raw_response.body,
83
108
  status: raw_response.code,
@@ -89,20 +114,27 @@ module TShield
89
114
  TShield::Response.new(raw_response.body, headers, raw_response.code)
90
115
  end
91
116
 
92
- def saved_content
93
- return @saved_content if @saved_content
94
-
95
- @saved_content = JSON.parse(File.open(headers_destiny).read)
96
- @saved_content['body'] = File.open(content_destiny).read unless @saved_content['body']
97
- @saved_content
117
+ def saved_content(session)
118
+ content = JSON.parse(File.open(headers_destiny(session)).read)
119
+ content['body'] = File.open(content_destiny(session)).read unless content['body']
120
+ content
98
121
  end
99
122
 
100
- def file_exists
101
- File.exist?(content_destiny)
123
+ def file_exists(session)
124
+ File.exist?(content_destiny(session))
102
125
  end
103
126
 
104
- def exists
105
- file_exists && configuration.cache_request?(domain)
127
+ def find_in_sessions
128
+ in_session = nil
129
+
130
+ ([@options[:session]] + (@options[:secondary_sessions] || [])).each do |session|
131
+ if file_exists(session) && configuration.cache_request?(domain)
132
+ in_session = (session || 'global')
133
+ break
134
+ end
135
+ TShield.logger.info("saved response not found in #{session}")
136
+ end
137
+ in_session
106
138
  end
107
139
 
108
140
  def key
@@ -127,9 +159,9 @@ module TShield
127
159
  if url.size > 225
128
160
  path = url.gsub(/(\?.*)/, '')
129
161
  params = Digest::SHA1.hexdigest Regexp.last_match(1)
130
- "#{path.gsub(%r{/}, '-').gsub(/^-/, '')}?#{params}"
162
+ "#{path.gsub(%r{/}, '-').gsub(/^-/, '')}#{configuration.get_questionmark_char}#{params}"
131
163
  else
132
- url.gsub(%r{/}, '-').gsub(/^-/, '')
164
+ url.gsub(%r{/}, '-').gsub(/^-/, '').gsub('?', configuration.get_questionmark_char)
133
165
  end
134
166
  end
135
167
  end
@@ -29,9 +29,9 @@ module TShield
29
29
 
30
30
  options '*' do
31
31
  response.headers['Allow'] = 'GET, PUT, POST, DELETE, OPTIONS'
32
- response.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Type,
33
- Accept, X-User-Email, X-Auth-Token'
32
+ response.headers['Access-Control-Allow-Headers'] = '*'
34
33
  response.headers['Access-Control-Allow-Origin'] = '*'
34
+ response.headers['Access-Control-Allow-Methods'] = '*'
35
35
  200
36
36
  end
37
37
 
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tshield/logger'
3
4
  require 'tshield/counter'
5
+ require 'tshield/errors'
4
6
 
5
7
  module TShield
6
8
  # Manage sessions
@@ -9,7 +11,11 @@ module TShield
9
11
  module Sessions
10
12
  def self.start(ip, name)
11
13
  TShield.logger.info("starting session #{name} for ip #{normalize_ip(ip)}")
12
- sessions[normalize_ip(ip)] = { name: name, counter: TShield::Counter.new }
14
+ sessions[normalize_ip(ip)] = {
15
+ name: name,
16
+ counter: TShield::Counter.new,
17
+ secondary_sessions: []
18
+ }
13
19
  end
14
20
 
15
21
  def self.stop(ip)
@@ -22,6 +28,16 @@ module TShield
22
28
  sessions[normalize_ip(ip)]
23
29
  end
24
30
 
31
+ def self.append(ip, name)
32
+ TShield.logger.info("appeding session #{name} for ip #{normalize_ip(ip)}")
33
+
34
+ current_session = sessions[normalize_ip(ip)]
35
+ raise AppendSessionWithoutMainSessionError, "not found main session for #{ip}" unless current_session
36
+
37
+ current_session[:secondary_sessions] << name
38
+ current_session
39
+ end
40
+
25
41
  def self.sessions
26
42
  @sessions ||= {}
27
43
  end
@@ -4,8 +4,8 @@ module TShield
4
4
  # Control version of gem
5
5
  class Version
6
6
  MAJOR = 0
7
- MINOR = 11
8
- PATCH = 19
7
+ MINOR = 13
8
+ PATCH = 2
9
9
  PRE = 0
10
10
 
11
11
  class << self
@@ -1,7 +1,9 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- require 'coveralls'
4
- Coveralls.wear!
3
+ if ENV['COVERALLS_REPO_TOKEN']
4
+ require 'coveralls'
5
+ Coveralls.wear!
6
+ end
5
7
 
6
8
  require 'bundler/setup'
7
9
  Bundler.setup
@@ -64,6 +64,33 @@ describe TShield::Configuration do
64
64
  expect(@configuration.get_domain_for('/api/four')).to be_nil
65
65
  end
66
66
  end
67
+
68
+ describe 'SO compatibility' do
69
+ it 'should be compatible with windows when configuration is true' do
70
+ allow(YAML).to receive(:safe_load).and_return({:windows_compatibility => true })
71
+ TShield::Configuration.clear
72
+ @configuration = TShield::Configuration.singleton
73
+
74
+ expect(@configuration.get_questionmark_char).to eq('%3f')
75
+ end
76
+
77
+ it 'should be compatible with Unix when configuration is false' do
78
+ allow(YAML).to receive(:safe_load).and_return({:windows_compatibility => false })
79
+ TShield::Configuration.clear
80
+ @configuration = TShield::Configuration.singleton
81
+
82
+ expect(@configuration.get_questionmark_char).to eq('?')
83
+ end
84
+
85
+ it 'should be compatible with Unix when configuration is missing' do
86
+ allow(YAML).to receive(:safe_load).and_return({})
87
+ TShield::Configuration.clear
88
+ @configuration = TShield::Configuration.singleton
89
+
90
+ expect(@configuration.get_questionmark_char).to eq('?')
91
+ end
92
+
93
+ end
67
94
  end
68
95
  context 'on config not exist' do
69
96
  before :each do
@@ -99,5 +99,17 @@
99
99
  "body": "body content in session"
100
100
  }
101
101
  }]
102
+ },
103
+ {
104
+ "session": "second-session",
105
+ "stubs": [{
106
+ "method": "GET",
107
+ "path": "/matching/second-example",
108
+ "response": {
109
+ "status": 200,
110
+ "headers": {},
111
+ "body": "body content in second-session"
112
+ }
113
+ }]
102
114
  }
103
115
  ]
@@ -235,6 +235,18 @@ describe TShield::RequestMatching do
235
235
  expect(@response.headers).to eql({})
236
236
  expect(@response.status).to eql(201)
237
237
  end
238
+ context 'with secondary session' do
239
+ it 'should return response object from session settings' do
240
+ @request_matching = TShield::RequestMatching.new('/matching/second-example',
241
+ method: 'GET',
242
+ session: 'a-session',
243
+ secondary_sessions: ['second-session'])
244
+ @response = @request_matching.match_request
245
+ expect(@response.body).to eql('body content in second-session')
246
+ expect(@response.headers).to eql({})
247
+ expect(@response.status).to eql(200)
248
+ end
249
+ end
238
250
  end
239
251
  end
240
252
  end
@@ -10,6 +10,7 @@ describe TShield::RequestVCR do
10
10
  allow(TShield::Configuration)
11
11
  .to receive(:singleton).and_return(@configuration)
12
12
  allow(@configuration).to receive(:get_before_filters).and_return([])
13
+ allow(@configuration).to receive(:not_save_headers).and_return([])
13
14
  allow(@configuration).to receive(:get_after_filters).and_return([])
14
15
  allow(@configuration).to receive(:request).and_return('timeout' => 10)
15
16
  allow(@configuration).to receive(:get_domain_for).and_return('example.org')
@@ -35,6 +36,24 @@ describe TShield::RequestVCR do
35
36
  TShield::RequestVCR.new '/', method: 'GET'
36
37
  end
37
38
 
39
+ it 'should write response headers as multiple occcurences when has more than one with same key' do
40
+ allow_any_instance_of(TShield::RequestVCR).to receive(:exists)
41
+ .and_return(false)
42
+ allow_any_instance_of(TShield::RequestVCR).to receive(:destiny)
43
+ allow(HTTParty).to receive(:send).and_return(RawResponseCookiesMultipleValues.new)
44
+
45
+ write_spy = double
46
+ allow(File).to receive(:open).and_return(write_spy)
47
+
48
+ expect(write_spy).to receive(:write).ordered.with('this is the body')
49
+ expect(write_spy).to receive(:write)
50
+ .ordered
51
+ .with("{\n \"status\": 200,\n \"headers\": {\n \"Set-Cookie\": [\n \"FirstCookie=An Value\",\n \"SecondCookie=An Value\"\n ]\n }\n}")
52
+ allow(write_spy).to receive(:close)
53
+
54
+ TShield::RequestVCR.new '/', method: 'GET'
55
+ end
56
+
38
57
  describe 'and query params exists in list to skip' do
39
58
  before :each do
40
59
  allow(@configuration).to receive(:get_name).and_return('example.org')
@@ -62,6 +81,8 @@ describe TShield::RequestVCR do
62
81
  }
63
82
  )
64
83
 
84
+ allow(@configuration).to receive(:get_questionmark_char).and_return('?')
85
+
65
86
  allow(HTTParty).to receive(:send).and_return(RawResponse.new)
66
87
  file_double = double
67
88
 
@@ -83,6 +104,53 @@ describe TShield::RequestVCR do
83
104
  method: 'GET',
84
105
  call: 0
85
106
  end
107
+
108
+ it 'should create response directory in windows standard' do
109
+
110
+ allow(@configuration).to receive(:domains).and_return(
111
+ 'example.org' => {
112
+ 'skip_query_params' => []
113
+ }
114
+ )
115
+
116
+ allow(@configuration).to receive(:get_questionmark_char).and_return('%3f')
117
+
118
+ allow(HTTParty).to receive(:send).and_return(RawResponse.new)
119
+ file_double = double
120
+
121
+ allow(File).to receive(:join)
122
+ .with('./requests/example.org', '%3fparam=value')
123
+ .and_return('./requests/example.org/%3fparam=value')
124
+ allow(File).to receive(:join)
125
+ .with('./requests/example.org/%3fparam=value', 'get')
126
+ .and_return('./requests/example.org/%3fparam=value/get')
127
+ allow(File).to receive(:join)
128
+ .with('./requests/example.org/%3fparam=value/get', '0')
129
+ .and_return('./requests/example.org/%3fparam=value/get/0')
130
+
131
+ allow(file_double).to receive(:read).and_return('{}')
132
+
133
+ expect(File).to receive('open')
134
+ .with('./requests/example.org/%3fparam=value/get/0.content', 'w')
135
+ .and_return(file_double)
136
+
137
+ expect(File).to receive('open')
138
+ .with('./requests/example.org/%3fparam=value/get/0.json', 'w')
139
+ .and_return(file_double)
140
+
141
+ expect(file_double).to receive(:write).ordered.with('this is the body')
142
+ expect(file_double).to receive(:write)
143
+ .with("{\n \"status\": 200,\n \"headers\": {\n }\n}")
144
+ expect(file_double).to receive(:close)
145
+ expect(file_double).to receive(:close)
146
+
147
+ TShield::RequestVCR.new '/',
148
+ raw_query: 'param=value',
149
+ method: 'GET',
150
+ call: 0
151
+ end
152
+
153
+
86
154
  end
87
155
  end
88
156
 
@@ -95,8 +163,33 @@ describe TShield::RequestVCR do
95
163
  'this is the body'
96
164
  end
97
165
 
166
+ def get_fields(field = "")
167
+ []
168
+ end
169
+
98
170
  def code
99
171
  200
100
172
  end
101
173
  end
174
+
175
+ class RawResponseCookiesMultipleValues
176
+ def headers
177
+ {'Set-Cookie' => ['FirstCookie=An Value', 'SecondCookie=An Value']}
178
+ end
179
+
180
+ def body
181
+ 'this is the body'
182
+ end
183
+
184
+ def get_fields(field = "")
185
+ self.headers[filed] unless self.headers.key?(field)
186
+ end
187
+
188
+ def code
189
+ 200
190
+ end
191
+ end
192
+
102
193
  end
194
+
195
+
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/sessions'
4
+ require 'spec_helper'
5
+
6
+ describe TShield::Sessions do
7
+ context 'on append session' do
8
+ it 'should raise error if not has a main session' do
9
+ expect { TShield::Sessions.append 'ip', 'secondary-session' }
10
+ .to raise_error(AppendSessionWithoutMainSessionError)
11
+ end
12
+ it 'should append if has main session' do
13
+ TShield::Sessions.start 'ip', 'main-session'
14
+ result = TShield::Sessions.append 'ip', 'secondary-session'
15
+ expect(result[:secondary_sessions]).to include('secondary-session')
16
+ end
17
+ end
18
+ end
@@ -22,19 +22,18 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.test_files = Dir['spec/**/*']
24
24
 
25
- s.required_ruby_version = '>= 2.3'
25
+ s.required_ruby_version = '>= 2.4'
26
26
 
27
- s.add_dependency('byebug', '~> 11.0', '>= 11.0.1')
28
27
  s.add_dependency('grpc', '~> 1.28', '>= 1.28.0')
29
28
  s.add_dependency('grpc-tools', '~> 1.28', '>= 1.28.0')
30
29
  s.add_dependency('httparty', '~> 0.14', '>= 0.14.0')
31
30
  s.add_dependency('json', '~> 2.0', '>= 2.0')
32
31
  s.add_dependency('puma', '~> 4.3', '>= 4.3.3')
33
- s.add_dependency('sinatra', '~> 1.4', '>= 1.4.0')
32
+ s.add_dependency('sinatra', '~> 2.1', '>= 2.1.0')
34
33
  s.add_dependency('sinatra-cross_origin', '~> 0.4.0', '>= 0.4')
35
- s.add_development_dependency('coveralls')
34
+ s.add_development_dependency('coveralls', '~> 0.8', '>= 0.8.23')
36
35
  s.add_development_dependency('cucumber', '~> 3.1', '>= 3.1.2')
37
- s.add_development_dependency('guard', '~> 2.15', '>= 2.15.0')
36
+ s.add_development_dependency('guard', '~> 2.16', '>= 2.16.2')
38
37
  s.add_development_dependency('guard-rspec', '~> 4.7', '>= 4.7.3')
39
38
  s.add_development_dependency('rake', '>= 10.0', '~> 13.0')
40
39
  s.add_development_dependency('rdoc', '~> 6.0', '>= 6.0')
@@ -42,6 +41,6 @@ Gem::Specification.new do |s|
42
41
  s.add_development_dependency('rspec', '~> 3.5', '>= 3.5.0')
43
42
  s.add_development_dependency('rubocop', '~> 0.73.0', '>= 0.73.0')
44
43
  s.add_development_dependency('rubocop-rails', '~> 2.2.0', '>= 2.2.1')
45
- s.add_development_dependency('simplecov', '~> 0.12', '>= 0.12.0')
44
+ s.add_development_dependency('simplecov', '~> 0.16', '>= 0.16.1')
46
45
  s.add_development_dependency('webmock', '~> 2.1', '>= 2.1.0')
47
46
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tshield
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.19.0
4
+ version: 0.13.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Rubin
@@ -9,106 +9,86 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-09 00:00:00.000000000 Z
12
+ date: 2021-01-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: byebug
15
+ name: grpc
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
18
  - - "~>"
19
19
  - !ruby/object:Gem::Version
20
- version: '11.0'
20
+ version: '1.28'
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 11.0.1
23
+ version: 1.28.0
24
24
  type: :runtime
25
25
  prerelease: false
26
26
  version_requirements: !ruby/object:Gem::Requirement
27
27
  requirements:
28
- - - "~>"
29
- - !ruby/object:Gem::Version
30
- version: '11.0'
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 11.0.1
34
- - !ruby/object:Gem::Dependency
35
- name: grpc
36
- requirement: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: 1.28.0
41
28
  - - "~>"
42
29
  - !ruby/object:Gem::Version
43
30
  version: '1.28'
44
- type: :runtime
45
- prerelease: false
46
- version_requirements: !ruby/object:Gem::Requirement
47
- requirements:
48
31
  - - ">="
49
32
  - !ruby/object:Gem::Version
50
33
  version: 1.28.0
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '1.28'
54
34
  - !ruby/object:Gem::Dependency
55
35
  name: grpc-tools
56
36
  requirement: !ruby/object:Gem::Requirement
57
37
  requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- version: 1.28.0
61
38
  - - "~>"
62
39
  - !ruby/object:Gem::Version
63
40
  version: '1.28'
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.28.0
64
44
  type: :runtime
65
45
  prerelease: false
66
46
  version_requirements: !ruby/object:Gem::Requirement
67
47
  requirements:
68
- - - ">="
69
- - !ruby/object:Gem::Version
70
- version: 1.28.0
71
48
  - - "~>"
72
49
  - !ruby/object:Gem::Version
73
50
  version: '1.28'
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.28.0
74
54
  - !ruby/object:Gem::Dependency
75
55
  name: httparty
76
56
  requirement: !ruby/object:Gem::Requirement
77
57
  requirements:
78
- - - ">="
79
- - !ruby/object:Gem::Version
80
- version: 0.14.0
81
58
  - - "~>"
82
59
  - !ruby/object:Gem::Version
83
60
  version: '0.14'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.14.0
84
64
  type: :runtime
85
65
  prerelease: false
86
66
  version_requirements: !ruby/object:Gem::Requirement
87
67
  requirements:
88
- - - ">="
89
- - !ruby/object:Gem::Version
90
- version: 0.14.0
91
68
  - - "~>"
92
69
  - !ruby/object:Gem::Version
93
70
  version: '0.14'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.14.0
94
74
  - !ruby/object:Gem::Dependency
95
75
  name: json
96
76
  requirement: !ruby/object:Gem::Requirement
97
77
  requirements:
98
- - - ">="
78
+ - - "~>"
99
79
  - !ruby/object:Gem::Version
100
80
  version: '2.0'
101
- - - "~>"
81
+ - - ">="
102
82
  - !ruby/object:Gem::Version
103
83
  version: '2.0'
104
84
  type: :runtime
105
85
  prerelease: false
106
86
  version_requirements: !ruby/object:Gem::Requirement
107
87
  requirements:
108
- - - ">="
88
+ - - "~>"
109
89
  - !ruby/object:Gem::Version
110
90
  version: '2.0'
111
- - - "~>"
91
+ - - ">="
112
92
  - !ruby/object:Gem::Version
113
93
  version: '2.0'
114
94
  - !ruby/object:Gem::Dependency
@@ -135,56 +115,62 @@ dependencies:
135
115
  name: sinatra
136
116
  requirement: !ruby/object:Gem::Requirement
137
117
  requirements:
138
- - - ">="
139
- - !ruby/object:Gem::Version
140
- version: 1.4.0
141
118
  - - "~>"
142
119
  - !ruby/object:Gem::Version
143
- version: '1.4'
120
+ version: '2.1'
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: 2.1.0
144
124
  type: :runtime
145
125
  prerelease: false
146
126
  version_requirements: !ruby/object:Gem::Requirement
147
127
  requirements:
148
- - - ">="
149
- - !ruby/object:Gem::Version
150
- version: 1.4.0
151
128
  - - "~>"
152
129
  - !ruby/object:Gem::Version
153
- version: '1.4'
130
+ version: '2.1'
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: 2.1.0
154
134
  - !ruby/object:Gem::Dependency
155
135
  name: sinatra-cross_origin
156
136
  requirement: !ruby/object:Gem::Requirement
157
137
  requirements:
158
- - - ">="
159
- - !ruby/object:Gem::Version
160
- version: '0.4'
161
138
  - - "~>"
162
139
  - !ruby/object:Gem::Version
163
140
  version: 0.4.0
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0.4'
164
144
  type: :runtime
165
145
  prerelease: false
166
146
  version_requirements: !ruby/object:Gem::Requirement
167
147
  requirements:
168
- - - ">="
169
- - !ruby/object:Gem::Version
170
- version: '0.4'
171
148
  - - "~>"
172
149
  - !ruby/object:Gem::Version
173
150
  version: 0.4.0
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0.4'
174
154
  - !ruby/object:Gem::Dependency
175
155
  name: coveralls
176
156
  requirement: !ruby/object:Gem::Requirement
177
157
  requirements:
158
+ - - "~>"
159
+ - !ruby/object:Gem::Version
160
+ version: '0.8'
178
161
  - - ">="
179
162
  - !ruby/object:Gem::Version
180
- version: '0'
163
+ version: 0.8.23
181
164
  type: :development
182
165
  prerelease: false
183
166
  version_requirements: !ruby/object:Gem::Requirement
184
167
  requirements:
168
+ - - "~>"
169
+ - !ruby/object:Gem::Version
170
+ version: '0.8'
185
171
  - - ">="
186
172
  - !ruby/object:Gem::Version
187
- version: '0'
173
+ version: 0.8.23
188
174
  - !ruby/object:Gem::Dependency
189
175
  name: cucumber
190
176
  requirement: !ruby/object:Gem::Requirement
@@ -209,22 +195,22 @@ dependencies:
209
195
  name: guard
210
196
  requirement: !ruby/object:Gem::Requirement
211
197
  requirements:
212
- - - ">="
213
- - !ruby/object:Gem::Version
214
- version: 2.15.0
215
198
  - - "~>"
216
199
  - !ruby/object:Gem::Version
217
- version: '2.15'
200
+ version: '2.16'
201
+ - - ">="
202
+ - !ruby/object:Gem::Version
203
+ version: 2.16.2
218
204
  type: :development
219
205
  prerelease: false
220
206
  version_requirements: !ruby/object:Gem::Requirement
221
207
  requirements:
222
- - - ">="
223
- - !ruby/object:Gem::Version
224
- version: 2.15.0
225
208
  - - "~>"
226
209
  - !ruby/object:Gem::Version
227
- version: '2.15'
210
+ version: '2.16'
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: 2.16.2
228
214
  - !ruby/object:Gem::Dependency
229
215
  name: guard-rspec
230
216
  requirement: !ruby/object:Gem::Requirement
@@ -269,80 +255,80 @@ dependencies:
269
255
  name: rdoc
270
256
  requirement: !ruby/object:Gem::Requirement
271
257
  requirements:
272
- - - ">="
258
+ - - "~>"
273
259
  - !ruby/object:Gem::Version
274
260
  version: '6.0'
275
- - - "~>"
261
+ - - ">="
276
262
  - !ruby/object:Gem::Version
277
263
  version: '6.0'
278
264
  type: :development
279
265
  prerelease: false
280
266
  version_requirements: !ruby/object:Gem::Requirement
281
267
  requirements:
282
- - - ">="
268
+ - - "~>"
283
269
  - !ruby/object:Gem::Version
284
270
  version: '6.0'
285
- - - "~>"
271
+ - - ">="
286
272
  - !ruby/object:Gem::Version
287
273
  version: '6.0'
288
274
  - !ruby/object:Gem::Dependency
289
275
  name: reek
290
276
  requirement: !ruby/object:Gem::Requirement
291
277
  requirements:
292
- - - ">="
278
+ - - "~>"
293
279
  - !ruby/object:Gem::Version
294
280
  version: 5.4.0
295
- - - "~>"
281
+ - - ">="
296
282
  - !ruby/object:Gem::Version
297
283
  version: 5.4.0
298
284
  type: :development
299
285
  prerelease: false
300
286
  version_requirements: !ruby/object:Gem::Requirement
301
287
  requirements:
302
- - - ">="
288
+ - - "~>"
303
289
  - !ruby/object:Gem::Version
304
290
  version: 5.4.0
305
- - - "~>"
291
+ - - ">="
306
292
  - !ruby/object:Gem::Version
307
293
  version: 5.4.0
308
294
  - !ruby/object:Gem::Dependency
309
295
  name: rspec
310
296
  requirement: !ruby/object:Gem::Requirement
311
297
  requirements:
312
- - - ">="
313
- - !ruby/object:Gem::Version
314
- version: 3.5.0
315
298
  - - "~>"
316
299
  - !ruby/object:Gem::Version
317
300
  version: '3.5'
301
+ - - ">="
302
+ - !ruby/object:Gem::Version
303
+ version: 3.5.0
318
304
  type: :development
319
305
  prerelease: false
320
306
  version_requirements: !ruby/object:Gem::Requirement
321
307
  requirements:
322
- - - ">="
323
- - !ruby/object:Gem::Version
324
- version: 3.5.0
325
308
  - - "~>"
326
309
  - !ruby/object:Gem::Version
327
310
  version: '3.5'
311
+ - - ">="
312
+ - !ruby/object:Gem::Version
313
+ version: 3.5.0
328
314
  - !ruby/object:Gem::Dependency
329
315
  name: rubocop
330
316
  requirement: !ruby/object:Gem::Requirement
331
317
  requirements:
332
- - - ">="
318
+ - - "~>"
333
319
  - !ruby/object:Gem::Version
334
320
  version: 0.73.0
335
- - - "~>"
321
+ - - ">="
336
322
  - !ruby/object:Gem::Version
337
323
  version: 0.73.0
338
324
  type: :development
339
325
  prerelease: false
340
326
  version_requirements: !ruby/object:Gem::Requirement
341
327
  requirements:
342
- - - ">="
328
+ - - "~>"
343
329
  - !ruby/object:Gem::Version
344
330
  version: 0.73.0
345
- - - "~>"
331
+ - - ">="
346
332
  - !ruby/object:Gem::Version
347
333
  version: 0.73.0
348
334
  - !ruby/object:Gem::Dependency
@@ -369,42 +355,42 @@ dependencies:
369
355
  name: simplecov
370
356
  requirement: !ruby/object:Gem::Requirement
371
357
  requirements:
372
- - - ">="
373
- - !ruby/object:Gem::Version
374
- version: 0.12.0
375
358
  - - "~>"
376
359
  - !ruby/object:Gem::Version
377
- version: '0.12'
360
+ version: '0.16'
361
+ - - ">="
362
+ - !ruby/object:Gem::Version
363
+ version: 0.16.1
378
364
  type: :development
379
365
  prerelease: false
380
366
  version_requirements: !ruby/object:Gem::Requirement
381
367
  requirements:
382
- - - ">="
383
- - !ruby/object:Gem::Version
384
- version: 0.12.0
385
368
  - - "~>"
386
369
  - !ruby/object:Gem::Version
387
- version: '0.12'
370
+ version: '0.16'
371
+ - - ">="
372
+ - !ruby/object:Gem::Version
373
+ version: 0.16.1
388
374
  - !ruby/object:Gem::Dependency
389
375
  name: webmock
390
376
  requirement: !ruby/object:Gem::Requirement
391
377
  requirements:
392
- - - ">="
393
- - !ruby/object:Gem::Version
394
- version: 2.1.0
395
378
  - - "~>"
396
379
  - !ruby/object:Gem::Version
397
380
  version: '2.1'
381
+ - - ">="
382
+ - !ruby/object:Gem::Version
383
+ version: 2.1.0
398
384
  type: :development
399
385
  prerelease: false
400
386
  version_requirements: !ruby/object:Gem::Requirement
401
387
  requirements:
402
- - - ">="
403
- - !ruby/object:Gem::Version
404
- version: 2.1.0
405
388
  - - "~>"
406
389
  - !ruby/object:Gem::Version
407
390
  version: '2.1'
391
+ - - ">="
392
+ - !ruby/object:Gem::Version
393
+ version: 2.1.0
408
394
  description: Proxy for mocks API responses
409
395
  email: rubin.diego@gmail.com
410
396
  executables:
@@ -426,6 +412,7 @@ files:
426
412
  - lib/tshield/controllers/requests.rb
427
413
  - lib/tshield/controllers/sessions.rb
428
414
  - lib/tshield/counter.rb
415
+ - lib/tshield/errors.rb
429
416
  - lib/tshield/extensions/string_extensions.rb
430
417
  - lib/tshield/grpc.rb
431
418
  - lib/tshield/grpc/vcr.rb
@@ -452,6 +439,7 @@ files:
452
439
  - spec/tshield/options_spec.rb
453
440
  - spec/tshield/request_matching_spec.rb
454
441
  - spec/tshield/request_vcr_spec.rb
442
+ - spec/tshield/sessions_spec.rb
455
443
  - tshield.gemspec
456
444
  homepage: https://github.com/diegorubin/tshield
457
445
  licenses:
@@ -465,28 +453,30 @@ required_ruby_version: !ruby/object:Gem::Requirement
465
453
  requirements:
466
454
  - - ">="
467
455
  - !ruby/object:Gem::Version
468
- version: '2.3'
456
+ version: '2.4'
469
457
  required_rubygems_version: !ruby/object:Gem::Requirement
470
458
  requirements:
471
459
  - - ">="
472
460
  - !ruby/object:Gem::Version
473
461
  version: '0'
474
462
  requirements: []
475
- rubygems_version: 3.0.8
463
+ rubyforge_project:
464
+ rubygems_version: 2.6.14.4
476
465
  signing_key:
477
466
  specification_version: 4
478
467
  summary: Proxy for mocks API responses
479
468
  test_files:
480
469
  - spec/spec_helper.rb
470
+ - spec/tshield/fixtures/matching/example.json
471
+ - spec/tshield/fixtures/config/tshield-without-grpc.yml
472
+ - spec/tshield/fixtures/config/tshield.yml
473
+ - spec/tshield/fixtures/filters/example_filter.rb
474
+ - spec/tshield/fixtures/proto/test_services_pb.rb
475
+ - spec/tshield/options_spec.rb
476
+ - spec/tshield/configuration_spec.rb
477
+ - spec/tshield/sessions_spec.rb
481
478
  - spec/tshield/request_matching_spec.rb
479
+ - spec/tshield/controllers/requests_spec.rb
482
480
  - spec/tshield/grpc_spec.rb
483
- - spec/tshield/configuration_spec.rb
484
481
  - spec/tshield/request_vcr_spec.rb
485
- - spec/tshield/controllers/requests_spec.rb
486
- - spec/tshield/options_spec.rb
487
482
  - spec/tshield/after_filter_spec.rb
488
- - spec/tshield/fixtures/matching/example.json
489
- - spec/tshield/fixtures/proto/test_services_pb.rb
490
- - spec/tshield/fixtures/filters/example_filter.rb
491
- - spec/tshield/fixtures/config/tshield.yml
492
- - spec/tshield/fixtures/config/tshield-without-grpc.yml