tcr 0.2.1 → 0.3.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
- SHA1:
3
- metadata.gz: 228eadaf6c734a9281952492ab146d7fee17e246
4
- data.tar.gz: 64045b81372a13b4d957ea02bd266b5c7c443d3a
2
+ SHA256:
3
+ metadata.gz: ab0a471bccd0a80a5d75bec6909dc521072b2ad666af34935f1d6ab596d9f2b4
4
+ data.tar.gz: eb6d9817d928173edd5354dc206fe7dd3e35a0f38ffde356e0e6e233e0a58431
5
5
  SHA512:
6
- metadata.gz: 1210aebd2ec013acb28546caeae5b29022e8cd7ddee1cb9e7814140c86d6e8fcc351798b10b2d2637baf104b7617719d3f28001bb3cde4cfee4821fa697625a5
7
- data.tar.gz: d05fc665286ace6c2b77c6f30890aff288e872344da943a2f5c09afcf468c8c7b82acbabff10f227092fc6322aabbbf9cf82a0c10cdc758b07e004533110d84f
6
+ metadata.gz: cedcebdd9373876a73534581ef013945ab20912ba86b71375b14de26043c135420f5140a2c1cf9d920afc9eacaa9b2f567d173c5d2525aa60db19ac97f43f606
7
+ data.tar.gz: d41afdda3c2d24c85e461db3247fed88ed5c9019e0929251c03b199b37dc3dc7a261796c3266041aea90ffbdf9b0c6053a2e627a9e7b7258382430db85df66a9
@@ -0,0 +1,47 @@
1
+ name: TCR Spec Suite
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ name: Test
11
+ runs-on: ubuntu-latest
12
+
13
+ strategy:
14
+ matrix:
15
+ ruby:
16
+ - "2.0.0"
17
+ - "2.1.9"
18
+ - "2.2.10"
19
+ - "2.3.7"
20
+ - "2.4.4"
21
+ - "2.5.1"
22
+ - "2.7.3"
23
+ - "3.0.1"
24
+
25
+ steps:
26
+ - uses: actions/checkout@v2
27
+
28
+ - name: Initial setup
29
+ uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+
33
+ - name: Install dependencies
34
+ run: bundle install
35
+
36
+ - name: Run tests
37
+ run: bundle exec rspec
38
+
39
+ testall:
40
+ if: ${{ always() }}
41
+ runs-on: ubuntu-latest
42
+ name: Test (matrix)
43
+ needs: [test]
44
+ steps:
45
+ - name: Check status of all test runs
46
+ if: ${{ needs.test.result != 'success' }}
47
+ run: exit 1
@@ -5,46 +5,55 @@ require 'thread'
5
5
 
6
6
  module TCR
7
7
  class RecordableTCPSocket
8
- attr_reader :live, :socket
9
- attr_accessor :recording
8
+ attr_reader :live, :socket, :recording
10
9
 
11
10
  def initialize(address, port, cassette)
12
11
  raise TCR::NoCassetteError.new unless TCR.cassette
13
12
 
14
13
  @read_lock = Queue.new
14
+ @recording = cassette.next_session
15
+ @live = cassette.recording?
15
16
 
16
- if cassette.recording?
17
- @live = true
18
- @socket = TCPSocket.real_open(address, port)
17
+ if live
18
+ begin
19
+ @socket = TCPSocket.real_open(address, port)
20
+ rescue => e
21
+ recording << ["error", Marshal.dump(e)]
22
+ raise
23
+ end
19
24
  else
20
- @live = false
21
25
  @closed = false
26
+ check_recording_for_errors
22
27
  end
23
- @recording = cassette.next_session
24
28
  end
25
29
 
26
- def read(bytes)
27
- _read(:read, bytes)
30
+ def read(*args)
31
+ _read(:read, args)
28
32
  end
29
33
 
30
34
  def getc(*args)
31
- _read(:getc, *args)
35
+ _read(:getc, args)
32
36
  end
33
37
 
34
38
  def gets(*args)
35
- _read(:gets, *args)
39
+ _read(:gets, args)
36
40
  end
37
41
 
38
42
  def read_nonblock(*args)
39
- _read(:read_nonblock, *args, blocking: false)
43
+ _read(:read_nonblock, args, blocking: false)
44
+ end
45
+
46
+ def print(str, *args)
47
+ _write(:print, str, args)
40
48
  end
41
49
 
42
- def print(str)
43
- _write(:print, str)
50
+ def write(str, *args)
51
+ _write(:write, str, args)
52
+ str.length
44
53
  end
45
54
 
46
- def write(str)
47
- _write(:write, str)
55
+ def write_nonblock(str, *args)
56
+ _write(:write_nonblock, str, args)
48
57
  str.length
49
58
  end
50
59
 
@@ -75,6 +84,10 @@ module TCR
75
84
 
76
85
  private
77
86
 
87
+ def check_recording_for_errors
88
+ raise Marshal.load(recording.first.last) if recording.first.first == "error"
89
+ end
90
+
78
91
  def _intercept_socket
79
92
  if @socket
80
93
  @socket = yield @socket
@@ -91,10 +104,10 @@ module TCR
91
104
  @read_lock << 1
92
105
  end
93
106
 
94
- def _write(method, data)
107
+ def _write(method, data, args)
95
108
  if live
96
109
  payload = data.dup if !data.is_a?(Symbol)
97
- @socket.__send__(method, payload)
110
+ _delegate_call(method, args.unshift(payload))
98
111
  recording << ["write", data.dup]
99
112
  else
100
113
  direction, data = recording.shift
@@ -103,14 +116,11 @@ module TCR
103
116
  end
104
117
  end
105
118
 
106
- def _read(method, *args)
107
- blocking = true
108
- if args.last.is_a?(::Hash)
109
- blocking = args.pop.fetch(:blocking, true)
110
- end
119
+ def _read(method, args, opts = {})
120
+ blocking = opts.fetch(:blocking, true)
111
121
 
112
122
  if live
113
- data = @socket.__send__(method, *args)
123
+ data = _delegate_call(method, args)
114
124
  payload = data.dup if !data.is_a?(Symbol)
115
125
  recording << ["read", payload]
116
126
  else
@@ -122,6 +132,15 @@ module TCR
122
132
  data
123
133
  end
124
134
 
135
+ def _delegate_call(method, args)
136
+ if RUBY_VERSION >= "2.7" && Hash === args.last
137
+ kwargs = args.pop
138
+ @socket.__send__(method, *args, **kwargs)
139
+ else
140
+ @socket.__send__(method, *args)
141
+ end
142
+ end
143
+
125
144
  def _ensure_direction(desired, actual)
126
145
  raise TCR::DirectionMismatchError.new("Expected to '#{desired}' but next in recording was '#{actual}'") unless desired == actual
127
146
  end
@@ -138,6 +157,14 @@ module TCR
138
157
  end
139
158
  end
140
159
 
160
+ def ssl_version
161
+ ""
162
+ end
163
+
164
+ def cipher
165
+ []
166
+ end
167
+
141
168
  def sync_close=(arg)
142
169
  true
143
170
  end
@@ -161,6 +188,9 @@ module TCR
161
188
  def session=(args)
162
189
  end
163
190
 
191
+ def hostname=(args)
192
+ end
193
+
164
194
  def io
165
195
  self
166
196
  end
data/lib/tcr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TCR
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/tcr.rb CHANGED
@@ -88,11 +88,11 @@ class Socket
88
88
  class << self
89
89
  alias_method :real_tcp, :tcp
90
90
 
91
- def tcp(host, port, socket_opts)
91
+ def tcp(host, port, *socket_opts)
92
92
  if TCR.configuration.hook_tcp_ports.include?(port)
93
93
  TCR::RecordableTCPSocket.new(host, port, TCR.cassette)
94
94
  else
95
- real_tcp(host, port, socket_opts)
95
+ real_tcp(host, port, *socket_opts)
96
96
  end
97
97
  end
98
98
  end
data/spec/spec_helper.rb CHANGED
@@ -19,7 +19,6 @@ end
19
19
 
20
20
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
21
21
  RSpec.configure do |config|
22
- config.treat_symbols_as_metadata_keys_with_true_values = true
23
22
  config.run_all_when_everything_filtered = true
24
23
  config.filter_run :focus
25
24
 
data/spec/tcr_spec.rb CHANGED
@@ -25,54 +25,91 @@ RSpec.describe TCR do
25
25
  end
26
26
 
27
27
  describe ".configuration" do
28
- it "has a default cassette location configured" do
29
- TCR.configuration.cassette_library_dir.should == "fixtures/tcr_cassettes"
30
- end
28
+ it "has a default cassette location configured" do
29
+ TCR.configuration.cassette_library_dir.should == "fixtures/tcr_cassettes"
30
+ end
31
31
 
32
- it "has an empty list of hook ports by default" do
33
- TCR.configuration.hook_tcp_ports.should == []
34
- end
32
+ it "has an empty list of hook ports by default" do
33
+ TCR.configuration.hook_tcp_ports.should == []
34
+ end
35
35
 
36
- it "defaults to erroring on read/write mismatch access" do
37
- TCR.configuration.block_for_reads.should be_falsey
38
- end
36
+ it "defaults to erroring on read/write mismatch access" do
37
+ TCR.configuration.block_for_reads.should be_falsey
38
+ end
39
39
 
40
- it "defaults to hit all to false" do
41
- TCR.configuration.hit_all.should be_falsey
42
- end
43
- end
40
+ it "defaults to hit all to false" do
41
+ TCR.configuration.hit_all.should be_falsey
42
+ end
43
+ end
44
44
 
45
45
  describe ".configure" do
46
- it "configures cassette location" do
47
- expect {
48
- TCR.configure { |c| c.cassette_library_dir = "some/dir" }
49
- }.to change{ TCR.configuration.cassette_library_dir }.from("fixtures/tcr_cassettes").to("some/dir")
46
+ context "with cassette_library_dir option" do
47
+ it "configures cassette location" do
48
+ expect {
49
+ TCR.configure { |c| c.cassette_library_dir = "some/dir" }
50
+ }.to change{ TCR.configuration.cassette_library_dir }.from("fixtures/tcr_cassettes").to("some/dir")
51
+ end
50
52
  end
51
53
 
52
- it "configures tcp ports to hook" do
53
- expect {
54
- TCR.configure { |c| c.hook_tcp_ports = [2525] }
55
- }.to change{ TCR.configuration.hook_tcp_ports }.from([]).to([2525])
54
+ context "with hook_tcp_ports option" do
55
+ it "configures tcp ports to hook" do
56
+ expect {
57
+ TCR.configure { |c| c.hook_tcp_ports = [2525] }
58
+ }.to change{ TCR.configuration.hook_tcp_ports }.from([]).to([2525])
59
+ end
56
60
  end
57
61
 
58
- it "configures allowing a blocking read mode" do
59
- expect {
62
+ context "with block_for_reads option" do
63
+ before(:each) {
64
+ TCR.configure { |c|
65
+ c.hook_tcp_ports = [9999]
66
+ c.cassette_library_dir = '.'
67
+ }
68
+ }
69
+
70
+ it "configures allowing a blocking read mode" do
71
+ expect {
72
+ TCR.configure { |c| c.block_for_reads = true }
73
+ }.to change{ TCR.configuration.block_for_reads }.from(false).to(true)
74
+ end
75
+
76
+ it "blocks read thread until data is available instead of raising mismatch error" do
60
77
  TCR.configure { |c| c.block_for_reads = true }
61
- }.to change{ TCR.configuration.block_for_reads }.from(false).to(true)
62
- end
78
+ reads = Queue.new
63
79
 
64
- it "configures to check if all sesstions was hit" do
65
- expect {
66
- TCR.configure { |c| c.hit_all = true }
67
- }.to change{ TCR.configuration.hit_all }.from(false).to(true)
80
+ TCR.use_cassette("spec/fixtures/block_for_reads") do
81
+ sock = TCPSocket.open("google.com", 9999)
82
+
83
+ t = Thread.new do
84
+ reads << sock.gets
85
+ end
86
+
87
+ expect(reads.size).to eq(0)
88
+ sock.print("hello\n")
89
+ t.value
90
+ expect(reads.size).to eq(1)
91
+ end
92
+ end
93
+
94
+ context "when disabled" do
95
+ it "raises mismatch error" do
96
+ TCR.use_cassette("spec/fixtures/block_for_reads") do
97
+ sock = TCPSocket.open("google.com", 9999)
98
+ expect {
99
+ Timeout::timeout(1) { sock.gets }
100
+ }.to raise_error(TCR::DirectionMismatchError)
101
+ end
102
+ end
103
+ end
68
104
  end
69
- end
70
105
 
71
- it "raises an error if you connect to a hooked port without using a cassette" do
72
- TCR.configure { |c| c.hook_tcp_ports = [2525] }
73
- expect {
74
- tcp_socket = TCPSocket.open("smtp.mandrillapp.com", 2525)
75
- }.to raise_error(TCR::NoCassetteError)
106
+ context "with hit_all option" do
107
+ it "configures to check if all sessions were hit" do
108
+ expect {
109
+ TCR.configure { |c| c.hit_all = true }
110
+ }.to change{ TCR.configuration.hit_all }.from(false).to(true)
111
+ end
112
+ end
76
113
  end
77
114
 
78
115
  describe ".turned_off" do
@@ -98,44 +135,6 @@ RSpec.describe TCR do
98
135
  end
99
136
  end
100
137
 
101
- describe "block_for_reads" do
102
- before(:each) {
103
- TCR.configure { |c|
104
- c.hook_tcp_ports = [9999]
105
- c.cassette_library_dir = '.'
106
- }
107
- }
108
-
109
- it "blocks read thread until data is available instead of raising mismatch error" do
110
- TCR.configure { |c| c.block_for_reads = true }
111
- reads = Queue.new
112
-
113
- TCR.use_cassette("spec/fixtures/block_for_reads") do
114
- sock = TCPSocket.open("google.com", 9999)
115
-
116
- t = Thread.new do
117
- reads << sock.gets
118
- end
119
-
120
- expect(reads.size).to eq(0)
121
- sock.print("hello\n")
122
- t.value
123
- expect(reads.size).to eq(1)
124
- end
125
- end
126
-
127
- context "when disabled" do
128
- it "raises mismatch error" do
129
- TCR.use_cassette("spec/fixtures/block_for_reads") do
130
- sock = TCPSocket.open("google.com", 9999)
131
- expect {
132
- Timeout::timeout(1) { sock.gets }
133
- }.to raise_error(TCR::DirectionMismatchError)
134
- end
135
- end
136
- end
137
- end
138
-
139
138
  describe ".use_cassette" do
140
139
  before(:each) {
141
140
  TCR.configure { |c|
@@ -144,6 +143,13 @@ RSpec.describe TCR do
144
143
  }
145
144
  }
146
145
 
146
+ it "MUST be used when connecting to hooked ports (or else raises an error)" do
147
+ TCR.configure { |c| c.hook_tcp_ports = [2525] }
148
+ expect {
149
+ tcp_socket = TCPSocket.open("smtp.mandrillapp.com", 2525)
150
+ }.to raise_error(TCR::NoCassetteError)
151
+ end
152
+
147
153
  it "requires a block to call" do
148
154
  expect {
149
155
  TCR.use_cassette("test")
@@ -416,9 +422,9 @@ RSpec.describe TCR do
416
422
  it "raises an error if you try to playback more sessions than you previously recorded" do
417
423
  expect {
418
424
  TCR.use_cassette("spec/fixtures/multitest-smtp") do
419
- smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525)
420
- smtp = Net::SMTP.start("mail.smtp2go.com", 2525)
421
- smtp = Net::SMTP.start("mail.smtp2go.com", 2525)
425
+ smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525, starttls: false)
426
+ smtp = Net::SMTP.start("mail.smtp2go.com", 2525, starttls: false)
427
+ smtp = Net::SMTP.start("mail.smtp2go.com", 2525, starttls: false)
422
428
  end
423
429
  }.to raise_error(TCR::NoMoreSessionsError)
424
430
  end
@@ -426,21 +432,47 @@ RSpec.describe TCR do
426
432
  it "raises an error if you try to playback less sessions than you previously recorded" do
427
433
  expect {
428
434
  TCR.use_cassette("spec/fixtures/multitest-extra-smtp", hit_all: true) do
429
- smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525)
435
+ smtp = Net::SMTP.start("smtp.mandrillapp.com", 2525, starttls: false)
430
436
  end
431
437
  }.to raise_error(TCR::ExtraSessionsError)
432
438
  end
433
439
  end
434
440
  end
435
441
 
442
+ context "when TCPSocket.open raises an error during recording" do
443
+ before do
444
+ TCR.configure { |c|
445
+ c.format = "yaml" # JSON borks on binary strings
446
+ c.hook_tcp_ports = [143]
447
+ c.cassette_library_dir = "."
448
+ }
449
+
450
+ # record cassette
451
+ TCR.use_cassette("test") do
452
+ expect { Net::IMAP.new(nil) }.to raise_error(SystemCallError)
453
+ end
454
+ end
455
+
456
+ it "records error to cassette" do
457
+ expect(File.exist?('test.yaml')).to be(true)
458
+ expect(File.read('test.yaml')).not_to be_empty
459
+ end
460
+
461
+ it "re-raises the error during replay" do
462
+ TCR.use_cassette("test") do
463
+ expect { Net::IMAP.new(nil) }.to raise_error(SystemCallError)
464
+ end
465
+ end
466
+ end
467
+
436
468
  it "replaces sockets created with Socket.tcp" do
437
469
  TCR.configure { |c|
438
- c.hook_tcp_ports = [23]
470
+ c.hook_tcp_ports = [80]
439
471
  c.cassette_library_dir = "."
440
472
  }
441
473
 
442
474
  TCR.use_cassette("test") do
443
- sock = Socket.tcp("towel.blinkenlights.nl", 23, {})
475
+ sock = Socket.tcp("google.com", 80)
444
476
  expect(sock).to be_a(TCR::RecordableTCPSocket)
445
477
  end
446
478
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tcr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Forman
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-20 00:00:00.000000000 Z
11
+ date: 2023-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -87,9 +87,9 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
+ - ".github/workflows/tcr.yml"
90
91
  - ".gitignore"
91
92
  - ".rvmrc"
92
- - ".travis.yml"
93
93
  - Gemfile
94
94
  - LICENSE.txt
95
95
  - README.md
@@ -118,7 +118,7 @@ files:
118
118
  homepage: ''
119
119
  licenses: []
120
120
  metadata: {}
121
- post_install_message:
121
+ post_install_message:
122
122
  rdoc_options: []
123
123
  require_paths:
124
124
  - lib
@@ -133,9 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
133
  - !ruby/object:Gem::Version
134
134
  version: '0'
135
135
  requirements: []
136
- rubyforge_project:
137
- rubygems_version: 2.4.5
138
- signing_key:
136
+ rubygems_version: 3.4.7
137
+ signing_key:
139
138
  specification_version: 4
140
139
  summary: TCR is a lightweight VCR for TCP sockets.
141
140
  test_files:
data/.travis.yml DELETED
@@ -1,11 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - "2.0.0"
4
- - "2.1.10"
5
- - "2.2.10"
6
- - "2.3.7"
7
- - "2.4.4"
8
- - "2.5.1"
9
- before_install:
10
- - gem install bundler
11
- script: bundle exec rspec