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.
- data/lib/tcr/cassette.rb +40 -0
- data/lib/tcr/errors.rb +1 -0
- data/lib/tcr/recordable_tcp_socket.rb +17 -16
- data/lib/tcr/version.rb +1 -1
- data/lib/tcr.rb +12 -11
- data/spec/fixtures/google_smtp.json +8 -0
- data/spec/fixtures/multitest-smtp.json +46 -0
- data/spec/fixtures/multitest.json +14 -0
- data/spec/tcr_spec.rb +62 -4
- metadata +8 -1
data/lib/tcr/cassette.rb
ADDED
@@ -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,28 +1,29 @@
|
|
1
1
|
module TCR
|
2
2
|
class RecordableTCPSocket
|
3
|
-
attr_reader :live, :
|
4
|
-
attr_accessor :
|
3
|
+
attr_reader :live, :cassette
|
4
|
+
attr_accessor :recording
|
5
5
|
|
6
|
-
def initialize(address, port,
|
7
|
-
|
6
|
+
def initialize(address, port, cassette)
|
7
|
+
raise TCR::NoCassetteError.new unless TCR.cassette
|
8
8
|
|
9
|
-
if
|
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
|
-
|
23
|
+
recording << ["read", data]
|
23
24
|
else
|
24
|
-
direction, data =
|
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
|
-
|
35
|
+
recording << ["write", str]
|
35
36
|
else
|
36
|
-
direction, data =
|
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
|
-
|
62
|
+
cassette.append(recording)
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
data/lib/tcr/version.rb
CHANGED
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
|
21
|
-
|
22
|
-
|
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
|
-
|
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.
|
48
|
+
TCR::RecordableTCPSocket.new(address, port, TCR.cassette)
|
48
49
|
else
|
49
50
|
real_open(address, port)
|
50
51
|
end
|
@@ -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
|
+
]
|
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("
|
48
|
+
File.unlink("test.json") if File.exists?("test.json")
|
48
49
|
}
|
49
50
|
after(:each) {
|
50
|
-
File.unlink("
|
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
|
-
|
76
|
-
|
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.
|
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
|