tcr 0.0.1 → 0.0.2

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.
@@ -0,0 +1,40 @@
1
+ module TCR
2
+ class Cassette
3
+ attr_reader :name
4
+
5
+ def initialize(name)
6
+ @name = name
7
+
8
+ if File.exists?(filename)
9
+ @recording = false
10
+ @contents = File.open(filename) { |f| f.read }
11
+ @sessions = JSON.parse(@contents)
12
+ else
13
+ @recording = true
14
+ @sessions = []
15
+ end
16
+ end
17
+
18
+ def recording?
19
+ @recording
20
+ end
21
+
22
+ def next_session
23
+ session = @sessions.shift
24
+ raise NoMoreSessionsError unless session
25
+ session
26
+ end
27
+
28
+ def append(session)
29
+ raise "Can't append session unless recording" unless recording?
30
+ @sessions << session
31
+ File.open(filename, "w") { |f| f.write(JSON.pretty_generate(@sessions)) }
32
+ end
33
+
34
+ protected
35
+
36
+ def filename
37
+ "#{TCR.configuration.cassette_library_dir}/#{name}.json"
38
+ end
39
+ end
40
+ end
data/lib/tcr/errors.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  module TCR
2
2
  class TCRError < StandardError; end
3
3
  class NoCassetteError < TCRError; end
4
+ class NoMoreSessionsError < TCRError; end
4
5
  class DirectionMismatchError < TCRError; end
5
6
  end
@@ -1,28 +1,29 @@
1
1
  module TCR
2
2
  class RecordableTCPSocket
3
- attr_reader :live, :recording_file
4
- attr_accessor :recordings
3
+ attr_reader :live, :cassette
4
+ attr_accessor :recording
5
5
 
6
- def initialize(address, port, recording_file)
7
- @recording_file = recording_file
6
+ def initialize(address, port, cassette)
7
+ raise TCR::NoCassetteError.new unless TCR.cassette
8
8
 
9
- if File.exists?(recording_file)
10
- @live = false
11
- @recordings = JSON.parse(File.open(recording_file, "r") { |f| f.read })
12
- else
9
+ if cassette.recording?
13
10
  @live = true
14
- @recordings = []
15
11
  @socket = TCPSocket.real_open(address, port)
12
+ @recording = []
13
+ else
14
+ @live = false
15
+ @recording = cassette.next_session
16
16
  end
17
+ @cassette = cassette
17
18
  end
18
19
 
19
20
  def read_nonblock(bytes)
20
21
  if live
21
22
  data = @socket.read_nonblock(bytes)
22
- recordings << ["read", data]
23
+ recording << ["read", data]
23
24
  else
24
- direction, data = recordings.shift
25
- raise DirectionMismatchError("Expected to 'read' but next in recording was 'write'") unless direction == "read"
25
+ direction, data = recording.shift
26
+ raise TCR::DirectionMismatchError.new("Expected to 'read' but next in recording was 'write'") unless direction == "read"
26
27
  end
27
28
 
28
29
  data
@@ -31,10 +32,10 @@ module TCR
31
32
  def write(str)
32
33
  if live
33
34
  len = @socket.write(str)
34
- recordings << ["write", str]
35
+ recording << ["write", str]
35
36
  else
36
- direction, data = recordings.shift
37
- raise DirectionMismatchError("Expected to 'write' but next in recording was 'read'") unless direction == "write"
37
+ direction, data = recording.shift
38
+ raise TCR::DirectionMismatchError.new("Expected to 'write' but next in recording was 'read'") unless direction == "write"
38
39
  len = data.length
39
40
  end
40
41
 
@@ -58,7 +59,7 @@ module TCR
58
59
  def close
59
60
  if live
60
61
  @socket.close
61
- File.open(recording_file, "w") { |f| f.write(JSON.pretty_generate(recordings)) }
62
+ cassette.append(recording)
62
63
  end
63
64
  end
64
65
  end
data/lib/tcr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module TCR
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/tcr.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "tcr/cassette"
1
2
  require "tcr/configuration"
2
3
  require "tcr/errors"
3
4
  require "tcr/recordable_tcp_socket"
@@ -17,23 +18,23 @@ module TCR
17
18
  @configuration ||= Configuration.new
18
19
  end
19
20
 
20
- def current_cassette
21
- raise TCR::NoCassetteError unless @current_cassette
22
- @current_cassette
21
+ def cassette
22
+ @cassette
23
+ end
24
+
25
+ def cassette=(v)
26
+ @cassette = v
27
+ end
28
+
29
+ def save_session
23
30
  end
24
31
 
25
32
  def use_cassette(name, options = {}, &block)
26
33
  raise ArgumentError, "`TCR.use_cassette` requires a block." unless block
27
- set_cassette(name)
34
+ TCR.cassette = Cassette.new(name)
28
35
  yield
29
36
  @current_cassette = nil
30
37
  end
31
-
32
- protected
33
-
34
- def set_cassette(name)
35
- @current_cassette = "#{TCR.configuration.cassette_library_dir}/#{name}.json"
36
- end
37
38
  end
38
39
 
39
40
 
@@ -44,7 +45,7 @@ class TCPSocket
44
45
 
45
46
  def open(address, port)
46
47
  if TCR.configuration.hook_tcp_ports.include?(port)
47
- TCR::RecordableTCPSocket.new(address, port, TCR.current_cassette)
48
+ TCR::RecordableTCPSocket.new(address, port, TCR.cassette)
48
49
  else
49
50
  real_open(address, port)
50
51
  end
@@ -0,0 +1,8 @@
1
+ [
2
+ [
3
+ [
4
+ "read",
5
+ "220 mx.google.com ESMTP x3si2474860qas.18 - gsmtp\r\n"
6
+ ]
7
+ ]
8
+ ]
@@ -0,0 +1,46 @@
1
+ [
2
+ [
3
+ [
4
+ "read",
5
+ "220 mx.google.com ESMTP d8si2472149qai.124 - gsmtp\r\n"
6
+ ],
7
+ [
8
+ "write",
9
+ "EHLO localhost\r\n"
10
+ ],
11
+ [
12
+ "read",
13
+ "250-mx.google.com at your service, [54.227.243.167]\r\n250-SIZE 35882577\r\n250-8BITMIME\r\n250-STARTTLS\r\n250 ENHANCEDSTATUSCODES\r\n"
14
+ ],
15
+ [
16
+ "write",
17
+ "QUIT\r\n"
18
+ ],
19
+ [
20
+ "read",
21
+ "221 2.0.0 closing connection d8si2472149qai.124 - gsmtp\r\n"
22
+ ]
23
+ ],
24
+ [
25
+ [
26
+ "read",
27
+ "220 mta1579.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n"
28
+ ],
29
+ [
30
+ "write",
31
+ "EHLO localhost\r\n"
32
+ ],
33
+ [
34
+ "read",
35
+ "250-mta1579.mail.gq1.yahoo.com\r\n250-8BITMIME\r\n250-SIZE 41943040\r\n250 PIPELINING\r\n"
36
+ ],
37
+ [
38
+ "write",
39
+ "QUIT\r\n"
40
+ ],
41
+ [
42
+ "read",
43
+ "221 mta1579.mail.gq1.yahoo.com\r\n"
44
+ ]
45
+ ]
46
+ ]
@@ -0,0 +1,14 @@
1
+ [
2
+ [
3
+ [
4
+ "read",
5
+ "220 mx.google.com ESMTP h5si2286277qec.54 - gsmtp\r\n"
6
+ ]
7
+ ],
8
+ [
9
+ [
10
+ "read",
11
+ "220 mta1009.mail.gq1.yahoo.com ESMTP YSmtpProxy service ready\r\n"
12
+ ]
13
+ ]
14
+ ]
data/spec/tcr_spec.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "spec_helper"
2
2
  require "tcr"
3
3
  require "net/protocol"
4
+ require "net/smtp"
4
5
 
5
6
  describe TCR do
6
7
  before(:each) do
@@ -44,10 +45,10 @@ describe TCR do
44
45
  c.hook_tcp_ports = [25]
45
46
  c.cassette_library_dir = "."
46
47
  }
47
- File.unlink("./test.json") if File.exists?("./test.json")
48
+ File.unlink("test.json") if File.exists?("test.json")
48
49
  }
49
50
  after(:each) {
50
- File.unlink("./test.json") if File.exists?("./test.json")
51
+ File.unlink("test.json") if File.exists?("test.json")
51
52
  }
52
53
 
53
54
  it "requires a block to call" do
@@ -72,8 +73,65 @@ describe TCR do
72
73
  line = io.readline
73
74
  tcp_socket.close
74
75
  end
75
- file_contents = File.open("./test.json") { |f| f.read }
76
- file_contents.include?("220 mx.google.com ESMTP").should == true
76
+ cassette_contents = File.open("test.json") { |f| f.read }
77
+ cassette_contents.include?("220 mx.google.com ESMTP").should == true
78
+ end
79
+
80
+ it "plays back tcp sessions without opening a real connection" do
81
+ expect(TCPSocket).to_not receive(:real_open)
82
+
83
+ TCR.use_cassette("spec/fixtures/google_smtp") do
84
+ tcp_socket = TCPSocket.open("aspmx.l.google.com", 25)
85
+ io = Net::InternetMessageIO.new(tcp_socket)
86
+ line = io.readline.should include("220 mx.google.com ESMTP")
87
+ end
88
+ end
89
+
90
+ it "raises an error if the recording gets out of order (i.e., we went to write but it expected a read)" do
91
+ expect {
92
+ TCR.use_cassette("spec/fixtures/google_smtp") do
93
+ tcp_socket = TCPSocket.open("aspmx.l.google.com", 25)
94
+ io = Net::InternetMessageIO.new(tcp_socket)
95
+ io.write("hi")
96
+ end
97
+ }.to raise_error(TCR::DirectionMismatchError)
98
+ end
99
+
100
+
101
+ context "multiple connections" do
102
+ it "records multiple sessions per cassette" do
103
+ TCR.use_cassette("test") do
104
+ smtp = Net::SMTP.start("aspmx.l.google.com", 25)
105
+ smtp.finish
106
+ smtp = Net::SMTP.start("mta6.am0.yahoodns.net", 25)
107
+ smtp.finish
108
+ end
109
+ cassette_contents = File.open("test.json") { |f| f.read }
110
+ cassette_contents.include?("google.com ESMTP").should == true
111
+ cassette_contents.include?("yahoo.com ESMTP").should == true
112
+ end
113
+
114
+ it "plays back multiple sessions per cassette in order" do
115
+ TCR.use_cassette("spec/fixtures/multitest") do
116
+ tcp_socket = TCPSocket.open("aspmx.l.google.com", 25)
117
+ io = Net::InternetMessageIO.new(tcp_socket)
118
+ line = io.readline.should include("google.com ESMTP")
119
+
120
+ tcp_socket = TCPSocket.open("mta6.am0.yahoodns.net", 25)
121
+ io = Net::InternetMessageIO.new(tcp_socket)
122
+ line = io.readline.should include("yahoo.com ESMTP")
123
+ end
124
+ end
125
+
126
+ it "raises an error if you try to playback more sessions than you previously recorded" do
127
+ expect {
128
+ TCR.use_cassette("spec/fixtures/multitest-smtp") do
129
+ smtp = Net::SMTP.start("aspmx.l.google.com", 25)
130
+ smtp = Net::SMTP.start("mta6.am0.yahoodns.net", 25)
131
+ smtp = Net::SMTP.start("mta6.am0.yahoodns.net", 25)
132
+ end
133
+ }.to raise_error(TCR::NoMoreSessionsError)
134
+ end
77
135
  end
78
136
  end
79
137
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tcr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -57,10 +57,14 @@ files:
57
57
  - README.md
58
58
  - Rakefile
59
59
  - lib/tcr.rb
60
+ - lib/tcr/cassette.rb
60
61
  - lib/tcr/configuration.rb
61
62
  - lib/tcr/errors.rb
62
63
  - lib/tcr/recordable_tcp_socket.rb
63
64
  - lib/tcr/version.rb
65
+ - spec/fixtures/google_smtp.json
66
+ - spec/fixtures/multitest-smtp.json
67
+ - spec/fixtures/multitest.json
64
68
  - spec/spec_helper.rb
65
69
  - spec/tcr_spec.rb
66
70
  - tcr.gemspec
@@ -89,5 +93,8 @@ signing_key:
89
93
  specification_version: 3
90
94
  summary: TCR is a lightweight VCR for TCP sockets.
91
95
  test_files:
96
+ - spec/fixtures/google_smtp.json
97
+ - spec/fixtures/multitest-smtp.json
98
+ - spec/fixtures/multitest.json
92
99
  - spec/spec_helper.rb
93
100
  - spec/tcr_spec.rb