tshield 0.11.16.0 → 0.12.0.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: 6430dfd31f893ec3e51fc02ad401ea29f9dbcfa09c9b34a30ef4e23b7b9a3216
4
- data.tar.gz: 1f021ab7cc8688ee6ab077140d34dd239a76a578c0c738b639a974d36d91d48d
3
+ metadata.gz: b027cb3ab381c02f5e74668df35812a9056f2ea67be29103905d3a965168a449
4
+ data.tar.gz: 71c06f7a53ba24daafbf1c42f16ed02d05d00be2f663b2c52ae39e67377aa241
5
5
  SHA512:
6
- metadata.gz: b86bd18dc24834098e199e179d33a92b74b29a55fa495685e2939196c6f4be9558a324ee7b63590363bf3c0ec5deb512073c172ac828a2eea6f19f3ed842d77d
7
- data.tar.gz: 597db12ad099b8024841fd6f6e9d5b9eb2954750d617382c95654a930273558b44c18fc4f8a3ea67b3d9d66a7820b1dc2ddfe462b2eac591de67683db3c213f3
6
+ metadata.gz: a8829db78bef723d0a9afab28bcbd3d52127d4fff48ea098482ebc3fff1bb017a751b0aef1315eb4eaf67094c836168c601e9fc93af444f05f86c50cade2be6f
7
+ data.tar.gz: a5f981a9d81ff8415f60e1d17572d56cf3d6112899b5a2e67bd17cebe69c1e49c48818c0ba6d4009e03e64476d10b7ccf338a4171d822b316e7305cf6a777c2c
data/README.md CHANGED
@@ -13,7 +13,7 @@ TShield is an open source proxy for mocks API responses.
13
13
  * REST
14
14
  * SOAP
15
15
  * Session manager to separate multiple scenarios (success, error, sucess variation, ...)
16
- gRPC [EXPERIMENTAL]
16
+ * gRPC [EXPERIMENTAL]
17
17
  * Lightweight
18
18
  * MIT license
19
19
 
@@ -68,6 +68,17 @@ domains:
68
68
  - /users
69
69
  ```
70
70
 
71
+ **Windows Compatibility:** If you need to use Tshield in Windows SO, change the config file and set the windows_compatibility to true.
72
+
73
+ Eg:
74
+ ```yaml
75
+ windows_compatibility: true
76
+ request:
77
+ # wait time for real service
78
+ timeout: 8
79
+ ...
80
+ ```
81
+
71
82
  ## Config options for Pattern Matching
72
83
 
73
84
  An example of file to create a stub:
@@ -250,6 +261,16 @@ _DELETE_ to http://localhost:4567/sessions
250
261
  curl -X DELETE \
251
262
  http://localhost:4567/sessions
252
263
  ```
264
+ ### Append secondary TShield session
265
+ **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.**
266
+
267
+ _POST_ to http://localhost:4567/sessions?name=<<same_name>>
268
+
269
+ ```
270
+ curl -X POST \
271
+ 'http://localhost:4567/sessions/append?name=my_valid'
272
+ ```
273
+
253
274
  ## [Experimental] Config options for gRPC
254
275
 
255
276
  ```yaml
@@ -265,7 +286,6 @@ grpc:
265
286
 
266
287
  ### Not Implemented Yet
267
288
 
268
- - Sessions
269
289
  - Matching
270
290
 
271
291
  ### Configuration
@@ -5,5 +5,6 @@ require 'tshield/options'
5
5
  TShield::Options.init
6
6
 
7
7
  require 'tshield'
8
- Thread.new { TShield::Server.run! }
9
- TShield::Grpc.run!
8
+
9
+ Thread.new { TShield::Grpc.run! }
10
+ TShield::Server.run!
@@ -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 || {})
@@ -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,7 +69,7 @@ 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
@@ -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
@@ -3,16 +3,15 @@
3
3
  require 'grpc'
4
4
 
5
5
  require 'tshield/configuration'
6
- require 'tshield/sessions'
7
6
  require 'tshield/grpc/vcr'
8
7
 
9
8
  module TShield
10
9
  module Grpc
11
10
  module RequestHandler
12
11
  include TShield::Grpc::VCR
13
- def handler(method_name, request)
12
+ def handler(method_name, request, parameters)
14
13
  options = self.class.options
15
- handler_in_vcr_mode(method_name, request, options)
14
+ handler_in_vcr_mode(method_name, request, parameters, options)
16
15
  end
17
16
  end
18
17
  def self.run!
@@ -24,6 +23,7 @@ module TShield
24
23
  TShield.logger.info("loading proto files from #{lib_dir}")
25
24
 
26
25
  bind = "0.0.0.0:#{@configuration['port']}"
26
+ TShield.logger.info("Starting gRPC server in #{bind}")
27
27
 
28
28
  server = GRPC::RpcServer.new
29
29
  server.add_http2_port(bind, :this_port_is_insecure)
@@ -61,8 +61,8 @@ module TShield
61
61
  descriptions.each do |service_name, description|
62
62
  puts description
63
63
  method_name = service_name.to_s.underscore.to_sym
64
- define_method(method_name) do |request, _unused_call|
65
- handler(__method__, request)
64
+ define_method(method_name) do |request, parameters|
65
+ handler(__method__, request, parameters)
66
66
  end
67
67
  end
68
68
  end
@@ -1,65 +1,82 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tshield/sessions'
4
+
3
5
  module TShield
4
6
  module Grpc
5
7
  module VCR
6
- def handler_in_vcr_mode(method_name, request, options)
8
+ def handler_in_vcr_mode(method_name, request, parameters, options)
9
+ parameters.peer =~ /ipv6:\[(.+?)\]|ipv4:(.+?):/
10
+ peer = Regexp.last_match(1) || Regexp.last_match(2)
11
+
12
+ TShield.logger.info("request from #{parameters.peer}")
13
+ @session = TShield::Sessions.current(peer)
14
+
15
+ TShield.logger.info("grpc using session #{@session || 'default'}")
7
16
  module_name = options['module']
8
17
 
9
- response = saved_response(module_name, method_name, request)
10
- return response if response
18
+ path = create_destiny(module_name, method_name, request)
19
+ response = saved_response(path)
20
+ if response
21
+ TShield.logger.info("returning saved response for request #{request.to_json} saved into #{hexdigest(request)}")
22
+ return response
23
+ end
11
24
 
25
+ TShield.logger.info("calling server to get response for #{request.to_json}")
12
26
  client_class = Object.const_get("#{module_name}::Stub")
13
27
  client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
14
28
  response = client_instance.send(method_name, request)
15
- save_request_and_response(request, response)
29
+ save_request_and_response(path, request, response)
16
30
  response
17
31
  end
18
32
 
19
- def saved_response(module_name, method_name, request)
20
- create_destiny(module_name, method_name, request)
21
- response_file = File.join(@complete_path, 'response')
33
+ def saved_response(path)
34
+ response_file = File.join(path, 'response')
22
35
  return false unless File.exist? response_file
23
36
 
24
37
  content = JSON.parse File.open(response_file).read
25
- response_class = File.open(File.join(@complete_path, 'response_class')).read.strip
38
+ response_class = File.open(File.join(path, 'response_class')).read.strip
26
39
  Kernel.const_get(response_class).new(content)
27
40
  end
28
41
 
29
- def save_request_and_response(request, response)
30
- save_request(request)
31
- save_response(response)
42
+ def save_request_and_response(path, request, response)
43
+ save_request(path, request)
44
+ save_response(path, response)
32
45
  end
33
46
 
34
- def save_request(request)
35
- file = File.open(File.join(@complete_path, 'original_request'), 'w')
47
+ def save_request(path, request)
48
+ file = File.open(File.join(path, 'original_request'), 'w')
36
49
  file.puts request.to_json
37
50
  file.close
38
51
  end
39
52
 
40
- def save_response(response)
41
- file = File.open(File.join(@complete_path, 'response'), 'w')
53
+ def save_response(path, response)
54
+ file = File.open(File.join(path, 'response'), 'w')
42
55
  file.puts response.to_json
43
56
  file.close
44
57
 
45
- response_class = File.open(File.join(@complete_path, 'response_class'), 'w')
58
+ response_class = File.open(File.join(path, 'response_class'), 'w')
46
59
  response_class.puts response.class.to_s
47
60
  response_class.close
48
61
  end
49
62
 
50
63
  def complete_path(module_name, method_name, request)
51
- return @complete_path if @complete_path
52
-
53
- @complete_path = ['requests', 'grpc', module_name, method_name.to_s, hexdigest(request)]
64
+ @session_name = (@session || {})[:name]
65
+ path = ['requests', 'grpc', @session_name, module_name, method_name.to_s, hexdigest(request)].compact
66
+ path
54
67
  end
55
68
 
56
69
  def create_destiny(module_name, method_name, request)
57
70
  current_path = []
58
- complete_path(module_name, method_name, request).each do |path|
71
+
72
+ path = complete_path(module_name, method_name, request)
73
+ TShield.logger.info("using path #{path}")
74
+ path.each do |path|
59
75
  current_path << path
60
76
  destiny = File.join current_path
61
77
  Dir.mkdir destiny unless File.exist? destiny
62
78
  end
79
+ path
63
80
  end
64
81
 
65
82
  def hexdigest(request)
@@ -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)
@@ -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
@@ -89,20 +95,27 @@ module TShield
89
95
  TShield::Response.new(raw_response.body, headers, raw_response.code)
90
96
  end
91
97
 
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
98
+ def saved_content(session)
99
+ content = JSON.parse(File.open(headers_destiny(session)).read)
100
+ content['body'] = File.open(content_destiny(session)).read unless content['body']
101
+ content
98
102
  end
99
103
 
100
- def file_exists
101
- File.exist?(content_destiny)
104
+ def file_exists(session)
105
+ File.exist?(content_destiny(session))
102
106
  end
103
107
 
104
- def exists
105
- file_exists && configuration.cache_request?(domain)
108
+ def find_in_sessions
109
+ in_session = nil
110
+
111
+ ([@options[:session]] + (@options[:secondary_sessions] || [])).each do |session|
112
+ if file_exists(session) && configuration.cache_request?(domain)
113
+ in_session = (session || 'global')
114
+ break
115
+ end
116
+ TShield.logger.info("saved response not found in #{session}")
117
+ end
118
+ in_session
106
119
  end
107
120
 
108
121
  def key
@@ -127,9 +140,9 @@ module TShield
127
140
  if url.size > 225
128
141
  path = url.gsub(/(\?.*)/, '')
129
142
  params = Digest::SHA1.hexdigest Regexp.last_match(1)
130
- "#{path.gsub(%r{/}, '-').gsub(/^-/, '')}?#{params}"
143
+ "#{path.gsub(%r{/}, '-').gsub(/^-/, '')}#{configuration.get_questionmark_char}#{params}"
131
144
  else
132
- url.gsub(%r{/}, '-').gsub(/^-/, '')
145
+ url.gsub(%r{/}, '-').gsub(/^-/, '').gsub('?', configuration.get_questionmark_char)
133
146
  end
134
147
  end
135
148
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'byebug'
3
+ require 'tshield/logger'
4
4
  require 'tshield/counter'
5
+ require 'tshield/errors'
5
6
 
6
7
  module TShield
7
8
  # Manage sessions
@@ -9,17 +10,34 @@ module TShield
9
10
  # Start and stop session for ip
10
11
  module Sessions
11
12
  def self.start(ip, name)
12
- sessions[normalize_ip(ip)] = { name: name, counter: TShield::Counter.new }
13
+ TShield.logger.info("starting session #{name} for ip #{normalize_ip(ip)}")
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
+ TShield.logger.info("stoping session for ip #{normalize_ip(ip)}")
16
23
  sessions[normalize_ip(ip)] = nil
17
24
  end
18
25
 
19
26
  def self.current(ip)
27
+ TShield.logger.info("fetching session for ip #{normalize_ip(ip)}")
20
28
  sessions[normalize_ip(ip)]
21
29
  end
22
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
+
23
41
  def self.sessions
24
42
  @sessions ||= {}
25
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 = 16
7
+ MINOR = 12
8
+ PATCH = 0
9
9
  PRE = 0
10
10
 
11
11
  class << self
@@ -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
@@ -62,6 +62,8 @@ describe TShield::RequestVCR do
62
62
  }
63
63
  )
64
64
 
65
+ allow(@configuration).to receive(:get_questionmark_char).and_return('?')
66
+
65
67
  allow(HTTParty).to receive(:send).and_return(RawResponse.new)
66
68
  file_double = double
67
69
 
@@ -83,6 +85,53 @@ describe TShield::RequestVCR do
83
85
  method: 'GET',
84
86
  call: 0
85
87
  end
88
+
89
+ it 'should create response directory in windows standard' do
90
+
91
+ allow(@configuration).to receive(:domains).and_return(
92
+ 'example.org' => {
93
+ 'skip_query_params' => []
94
+ }
95
+ )
96
+
97
+ allow(@configuration).to receive(:get_questionmark_char).and_return('%3f')
98
+
99
+ allow(HTTParty).to receive(:send).and_return(RawResponse.new)
100
+ file_double = double
101
+
102
+ allow(File).to receive(:join)
103
+ .with('./requests/example.org', '%3fparam=value')
104
+ .and_return('./requests/example.org/%3fparam=value')
105
+ allow(File).to receive(:join)
106
+ .with('./requests/example.org/%3fparam=value', 'get')
107
+ .and_return('./requests/example.org/%3fparam=value/get')
108
+ allow(File).to receive(:join)
109
+ .with('./requests/example.org/%3fparam=value/get', '0')
110
+ .and_return('./requests/example.org/%3fparam=value/get/0')
111
+
112
+ allow(file_double).to receive(:read).and_return('{}')
113
+
114
+ expect(File).to receive('open')
115
+ .with('./requests/example.org/%3fparam=value/get/0.content', 'w')
116
+ .and_return(file_double)
117
+
118
+ expect(File).to receive('open')
119
+ .with('./requests/example.org/%3fparam=value/get/0.json', 'w')
120
+ .and_return(file_double)
121
+
122
+ expect(file_double).to receive(:write).ordered.with('this is the body')
123
+ expect(file_double).to receive(:write)
124
+ .with("{\n \"status\": 200,\n \"headers\": {\n }\n}")
125
+ expect(file_double).to receive(:close)
126
+ expect(file_double).to receive(:close)
127
+
128
+ TShield::RequestVCR.new '/',
129
+ raw_query: 'param=value',
130
+ method: 'GET',
131
+ call: 0
132
+ end
133
+
134
+
86
135
  end
87
136
  end
88
137
 
@@ -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.16.0
4
+ version: 0.12.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Rubin
@@ -9,28 +9,8 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-08 00:00:00.000000000 Z
12
+ date: 2020-11-03 00:00:00.000000000 Z
13
13
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: byebug
16
- requirement: !ruby/object:Gem::Requirement
17
- requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '11.0'
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 11.0.1
24
- type: :runtime
25
- prerelease: false
26
- version_requirements: !ruby/object:Gem::Requirement
27
- requirements:
28
- - - "~>"
29
- - !ruby/object:Gem::Version
30
- version: '11.0'
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 11.0.1
34
14
  - !ruby/object:Gem::Dependency
35
15
  name: grpc
36
16
  requirement: !ruby/object:Gem::Requirement
@@ -137,20 +117,20 @@ dependencies:
137
117
  requirements:
138
118
  - - ">="
139
119
  - !ruby/object:Gem::Version
140
- version: 1.4.0
120
+ version: 2.1.0
141
121
  - - "~>"
142
122
  - !ruby/object:Gem::Version
143
- version: '1.4'
123
+ version: '2.1'
144
124
  type: :runtime
145
125
  prerelease: false
146
126
  version_requirements: !ruby/object:Gem::Requirement
147
127
  requirements:
148
128
  - - ">="
149
129
  - !ruby/object:Gem::Version
150
- version: 1.4.0
130
+ version: 2.1.0
151
131
  - - "~>"
152
132
  - !ruby/object:Gem::Version
153
- version: '1.4'
133
+ version: '2.1'
154
134
  - !ruby/object:Gem::Dependency
155
135
  name: sinatra-cross_origin
156
136
  requirement: !ruby/object:Gem::Requirement
@@ -175,16 +155,22 @@ dependencies:
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
@@ -369,22 +355,22 @@ 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
@@ -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,7 +453,7 @@ 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
  - - ">="
@@ -481,6 +469,7 @@ test_files:
481
469
  - spec/tshield/request_matching_spec.rb
482
470
  - spec/tshield/grpc_spec.rb
483
471
  - spec/tshield/configuration_spec.rb
472
+ - spec/tshield/sessions_spec.rb
484
473
  - spec/tshield/request_vcr_spec.rb
485
474
  - spec/tshield/controllers/requests_spec.rb
486
475
  - spec/tshield/options_spec.rb