freshbooks.rb 3.0.13

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.
Files changed (46) hide show
  1. data/History.txt +8 -0
  2. data/LICENSE +10 -0
  3. data/Manifest.txt +45 -0
  4. data/README +44 -0
  5. data/Rakefile +27 -0
  6. data/lib/freshbooks.rb +94 -0
  7. data/lib/freshbooks/base.rb +169 -0
  8. data/lib/freshbooks/category.rb +11 -0
  9. data/lib/freshbooks/client.rb +22 -0
  10. data/lib/freshbooks/connection.rb +153 -0
  11. data/lib/freshbooks/estimate.rb +15 -0
  12. data/lib/freshbooks/expense.rb +12 -0
  13. data/lib/freshbooks/invoice.rb +18 -0
  14. data/lib/freshbooks/item.rb +11 -0
  15. data/lib/freshbooks/line.rb +10 -0
  16. data/lib/freshbooks/links.rb +7 -0
  17. data/lib/freshbooks/list_proxy.rb +80 -0
  18. data/lib/freshbooks/payment.rb +13 -0
  19. data/lib/freshbooks/project.rb +12 -0
  20. data/lib/freshbooks/recurring.rb +15 -0
  21. data/lib/freshbooks/response.rb +25 -0
  22. data/lib/freshbooks/schema/definition.rb +20 -0
  23. data/lib/freshbooks/schema/mixin.rb +40 -0
  24. data/lib/freshbooks/staff.rb +13 -0
  25. data/lib/freshbooks/task.rb +12 -0
  26. data/lib/freshbooks/time_entry.rb +12 -0
  27. data/lib/freshbooks/xml_serializer.rb +17 -0
  28. data/lib/freshbooks/xml_serializer/serializers.rb +109 -0
  29. data/script/console +10 -0
  30. data/script/destroy +14 -0
  31. data/script/generate +14 -0
  32. data/test/fixtures/freshbooks_credentials.sample.yml +3 -0
  33. data/test/fixtures/invoice_create_response.xml +4 -0
  34. data/test/fixtures/invoice_get_response.xml +54 -0
  35. data/test/fixtures/invoice_list_response.xml +109 -0
  36. data/test/fixtures/success_response.xml +2 -0
  37. data/test/mock_connection.rb +13 -0
  38. data/test/schema/test_definition.rb +36 -0
  39. data/test/schema/test_mixin.rb +39 -0
  40. data/test/test_base.rb +97 -0
  41. data/test/test_connection.rb +145 -0
  42. data/test/test_helper.rb +48 -0
  43. data/test/test_invoice.rb +125 -0
  44. data/test/test_list_proxy.rb +60 -0
  45. data/test/test_page.rb +50 -0
  46. metadata +148 -0
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/freshbooks.rb'}"
9
+ puts "Loading freshbooks.rb gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,3 @@
1
+ fresh_books_test_account:
2
+ account_url: <insert your account url>
3
+ api_key: <insert your api key>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <response status="ok">
3
+ <invoice_id>1</invoice_id>
4
+ </response>
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0"?>
2
+ <response status="ok">
3
+ <invoice>
4
+ <invoice_id>1</invoice_id>
5
+ <client_id>1</client_id>
6
+ <number>number1</number>
7
+ <recurring_id>1</recurring_id>
8
+ <organization>Organization 1</organization>
9
+ <status>draft</status>
10
+ <amount>100.00</amount>
11
+ <amount_outstanding>50.00</amount_outstanding>
12
+ <date>2009-02-01</date>
13
+
14
+ <po_number>1</po_number>
15
+ <discount>1</discount>
16
+ <notes>notes1</notes>
17
+ <terms>terms1</terms>
18
+
19
+ <first_name>first_name1</first_name>
20
+ <last_name>last_name1</last_name>
21
+ <p_street1>p_street11</p_street1>
22
+ <p_street2>p_street21</p_street2>
23
+ <p_city>p_city1</p_city>
24
+ <p_state>p_state1</p_state>
25
+ <p_country>p_country1</p_country>
26
+ <p_code>p_code1</p_code>
27
+
28
+ <return_uri>return_uri1</return_uri>
29
+ <updated>2009-08-1 01:00:00</updated>
30
+
31
+ <links>
32
+ <client_view>client_view1</client_view>
33
+ <view>view1</view>
34
+ <edit>edit1</edit>
35
+ </links>
36
+
37
+ <lines>
38
+ <line>
39
+ <amount>1</amount>
40
+ <name>name1</name>
41
+ <description>description1</description>
42
+ <unit_cost>1</unit_cost>
43
+ <quantity>1</quantity>
44
+ <tax1_name>tax1_name1</tax1_name>
45
+ <tax2_name>tax2_name1</tax2_name>
46
+ <tax1_percent>1</tax1_percent>
47
+ <tax2_percent>1</tax2_percent>
48
+ </line>
49
+ </lines>
50
+
51
+ <url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b</url>
52
+ <auth_url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b-z</auth_url>
53
+ </invoice>
54
+ </response>
@@ -0,0 +1,109 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <response status="ok">
3
+ <invoices page="1" per_page="2" pages="2" total="3">
4
+ <invoice>
5
+ <invoice_id>1</invoice_id>
6
+ <number>number1</number>
7
+ <client_id>1</client_id>
8
+ <recurring_id>1</recurring_id>
9
+ <organization>Organization 1</organization>
10
+ <status>draft</status>
11
+ <amount>100.00</amount>
12
+ <amount_outstanding>50.00</amount_outstanding>
13
+ <date>2009-02-01</date>
14
+
15
+ <po_number>1</po_number>
16
+ <discount>1</discount>
17
+ <notes>notes1</notes>
18
+ <terms>terms1</terms>
19
+
20
+ <first_name>first_name1</first_name>
21
+ <last_name>last_name1</last_name>
22
+ <p_street1>p_street11</p_street1>
23
+ <p_street2>p_street21</p_street2>
24
+ <p_city>p_city1</p_city>
25
+ <p_state>p_state1</p_state>
26
+ <p_country>p_country1</p_country>
27
+ <p_code>p_code1</p_code>
28
+
29
+ <return_uri>return_uri1</return_uri>
30
+ <updated>2009-08-1 01:00:00</updated>
31
+
32
+ <links>
33
+ <client_view>client_view1</client_view>
34
+ <view>view1</view>
35
+ <edit>edit1</edit>
36
+ </links>
37
+
38
+ <lines>
39
+ <line>
40
+ <amount>1</amount>
41
+ <name>name1</name>
42
+ <description>description1</description>
43
+ <unit_cost>1</unit_cost>
44
+ <quantity>1</quantity>
45
+ <tax1_name>tax1_name1</tax1_name>
46
+ <tax2_name>tax2_name1</tax2_name>
47
+ <tax1_percent>1</tax1_percent>
48
+ <tax2_percent>1</tax2_percent>
49
+ </line>
50
+ </lines>
51
+
52
+ <url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b</url>
53
+ <auth_url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b-z</auth_url>
54
+
55
+ </invoice>
56
+ <invoice>
57
+ <invoice_id>2</invoice_id>
58
+ <number>number2</number>
59
+ <client_id>2</client_id>
60
+ <recurring_id>2</recurring_id>
61
+ <organization>Organization 2</organization>
62
+ <status>draft</status>
63
+ <amount>200.00</amount>
64
+ <amount_outstanding>100.00</amount_outstanding>
65
+ <date>2009-02-02</date>
66
+
67
+ <po_number>2</po_number>
68
+ <discount>2</discount>
69
+ <notes>notes2</notes>
70
+ <terms>terms2</terms>
71
+
72
+ <first_name>first_name2</first_name>
73
+ <last_name>last_name2</last_name>
74
+ <p_street1>p_street12</p_street1>
75
+ <p_street2>p_street22</p_street2>
76
+ <p_city>p_city2</p_city>
77
+ <p_state>p_state2</p_state>
78
+ <p_country>p_country2</p_country>
79
+ <p_code>p_code2</p_code>
80
+
81
+ <return_uri>return_uri2</return_uri>
82
+ <updated>2009-08-2 02:00:00</updated>
83
+
84
+ <links>
85
+ <client_view>client_view2</client_view>
86
+ <view>view2</view>
87
+ <edit>edit2</edit>
88
+ </links>
89
+
90
+ <lines>
91
+ <line>
92
+ <amount>1</amount>
93
+ <name>name1</name>
94
+ <description>description1</description>
95
+ <unit_cost>1</unit_cost>
96
+ <quantity>1</quantity>
97
+ <tax1_name>tax1_name1</tax1_name>
98
+ <tax2_name>tax2_name1</tax2_name>
99
+ <tax1_percent>1</tax1_percent>
100
+ <tax2_percent>1</tax2_percent>
101
+ </line>
102
+ </lines>
103
+
104
+ <url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b</url>
105
+ <auth_url deprecated="true">https://sample.freshbooks.com/inv/12345-1-6d30b-z</auth_url>
106
+
107
+ </invoice>
108
+ </invoices>
109
+ </response>
@@ -0,0 +1,2 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <response status="ok"/>
@@ -0,0 +1,13 @@
1
+ require 'freshbooks/connection'
2
+
3
+ class MockConnection < FreshBooks::Connection
4
+ def initialize(response_body)
5
+ @response_body = response_body
6
+ end
7
+
8
+ protected
9
+
10
+ def post(request_body)
11
+ @response_body
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ module Schema
4
+ class TestDefinition < Test::Unit::TestCase
5
+ def setup
6
+ @definition = FreshBooks::Schema::Definition.new
7
+ end
8
+
9
+ def test_method_missing
10
+ # Empty
11
+ assert_equal 0, @definition.members.size
12
+
13
+ # One type
14
+ @definition.string :name
15
+ assert_equal 1, @definition.members.size
16
+ assert_equal({ :type => :string, :read_only => false }, @definition.members["name"])
17
+
18
+ # Multiple attributes
19
+ @definition.fixnum :version, :po_number
20
+ assert_equal 3, @definition.members.size
21
+ assert_equal({ :type => :fixnum, :read_only => false }, @definition.members["version"])
22
+ assert_equal({ :type => :fixnum, :read_only => false }, @definition.members["po_number"])
23
+
24
+ # Multiple times
25
+ @definition.fixnum :lock_number
26
+ assert_equal 4, @definition.members.size
27
+ assert_equal({ :type => :fixnum, :read_only => false }, @definition.members["lock_number"])
28
+ end
29
+
30
+ def test_method_missing_extra_options
31
+ @definition.fixnum :version, :po_number, :read_only => true
32
+ assert_equal({ :type => :fixnum, :read_only => true }, @definition.members["version"])
33
+ assert_equal({ :type => :fixnum, :read_only => true }, @definition.members["po_number"])
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/../test_helper.rb'
2
+
3
+ module Schema
4
+ class TestMixin < Test::Unit::TestCase
5
+ def test_define_schema__unique_definition_per_class
6
+ assert MyItem.schema_definition.members.include?("name")
7
+ assert !MyItem.schema_definition.members.include?("name2")
8
+
9
+ assert MyItem2.schema_definition.members.include?("name2")
10
+ assert !MyItem2.schema_definition.members.include?("name")
11
+ end
12
+
13
+ def test_define_schema__creates_attr_accessors
14
+ assert MyItem.public_method_defined?("name")
15
+ assert MyItem.public_method_defined?("name=")
16
+
17
+ assert MyItem2.public_method_defined?("name2")
18
+ assert MyItem2.public_method_defined?("name2=")
19
+ end
20
+
21
+ def test_define_schema__creates_read_only_attr_accessors
22
+ assert MyItem.public_method_defined?("read_only_name")
23
+ assert MyItem.protected_method_defined?("read_only_name=")
24
+ end
25
+ end
26
+
27
+ class MyItem < FreshBooks::Base
28
+ define_schema do |s|
29
+ s.string :name
30
+ s.string :read_only_name, :read_only => true
31
+ end
32
+ end
33
+
34
+ class MyItem2 < FreshBooks::Base
35
+ define_schema do |s|
36
+ s.string :name2
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,97 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestBase < Test::Unit::TestCase
4
+ def test_establish_connection
5
+ assert_nil FreshBooks::Base.connection
6
+
7
+ FreshBooks::Base.establish_connection("company.freshbooks.com", "auth_token")
8
+ connection = FreshBooks::Base.connection
9
+ assert_not_nil connection
10
+ assert_equal "company.freshbooks.com", connection.account_url
11
+ assert_equal "auth_token", connection.auth_token
12
+
13
+ FreshBooks::Base.establish_connection("company2.freshbooks.com", "auth_token2")
14
+ connection = FreshBooks::Base.connection
15
+ assert_not_nil connection
16
+ assert_equal "company2.freshbooks.com", connection.account_url
17
+ assert_equal "auth_token2", connection.auth_token
18
+ end
19
+
20
+ def test_new_from_xml_to_xml__round_trip
21
+ xml = <<-EOS
22
+ <my_item>
23
+ <name>name1</name>
24
+ <amount>4.5</amount>
25
+ <read_only_number>5</read_only_number>
26
+ <number>6</number>
27
+ <visible>1</visible>
28
+ <date>2008-02-01</date>
29
+ <created_at>2008-10-22 13:57:00</created_at>
30
+ <my_address>
31
+ <street1>street1</street1>
32
+ </my_address>
33
+ <my_lines>
34
+ <my_line>
35
+ <description>description</description>
36
+ </my_line>
37
+ </my_lines>
38
+ </my_item>
39
+ EOS
40
+ doc = REXML::Document.new(xml)
41
+
42
+ item = FreshBooks::MyItem.new_from_xml(doc.root)
43
+ assert_equal "name1", item.name
44
+ assert_equal 5, item.read_only_number
45
+ assert_equal 6, item.number
46
+ assert_equal 4.5, item.amount
47
+ assert_equal true, item.visible
48
+ assert_equal Date.new(2008, 2, 1), item.date
49
+ assert_equal DateTime.parse("2008-10-22 13:57:00 -04:00"), item.created_at
50
+ assert_equal "street1", item.my_address.street1
51
+ assert_equal 1, item.my_lines.size
52
+ assert_equal "description", item.my_lines.first.description
53
+
54
+ # If someone knows a good way to compare xml docs where ordering doesn't matter
55
+ # let me know. This can be refactored and improved greatly.
56
+ xml_out = item.to_xml.to_s.strip
57
+ assert_equal "<my_item>", xml_out.first(9)
58
+ assert xml_out.include?("<name>name1</name>")
59
+ assert xml_out.include?("<amount>4.5</amount>")
60
+ assert xml_out.include?("<visible>1</visible>")
61
+ assert !xml_out.include?("<read_only_number>5</read_only_number>") # this is read only
62
+ assert xml_out.include?("<number>6</number>")
63
+ assert xml_out.include?("<date>2008-02-01</date>")
64
+ assert xml_out.include?("<created_at>2008-10-22 13:57:00</created_at>")
65
+ assert xml_out.include?("<my_address><street1>street1</street1></my_address>")
66
+ assert xml_out.include?("<my_lines><my_line><description>description</description></my_line></my_lines>")
67
+ end
68
+
69
+ end
70
+
71
+ module FreshBooks
72
+ class MyItem < FreshBooks::Base
73
+ define_schema do |s|
74
+ s.string :name
75
+ s.fixnum :read_only_number, :read_only => true
76
+ s.fixnum :number
77
+ s.float :amount
78
+ s.boolean :visible
79
+ s.date :date
80
+ s.date_time :created_at
81
+ s.object :my_address
82
+ s.array :my_lines
83
+ end
84
+ end
85
+
86
+ class MyAddress < FreshBooks::Base
87
+ define_schema do |s|
88
+ s.string :street1
89
+ end
90
+ end
91
+
92
+ class MyLine < FreshBooks::Base
93
+ define_schema do |s|
94
+ s.string :description
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,145 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestConnection < Test::Unit::TestCase
4
+ def setup
5
+ @connection = FreshBooks::Connection.new("company.freshbooks.com", "auth_token")
6
+ end
7
+
8
+ def test_connection_accessors
9
+ assert_equal "company.freshbooks.com", @connection.account_url
10
+ assert_equal "auth_token", @connection.auth_token
11
+ end
12
+
13
+ def test_connection_request_headers
14
+ request_headers = { "key" => "value" }
15
+ connection = FreshBooks::Connection.new("company.freshbooks.com", "auth_token", request_headers)
16
+ assert_equal request_headers, connection.request_headers
17
+ end
18
+
19
+
20
+ def test_create_request__array_of_elements
21
+ request = @connection.send(:create_request, 'mymethod', [['element1', 'value1'], [:element2, :value2]])
22
+ assert_equal "<?xml version='1.0' encoding='UTF-8'?><request method='mymethod'><element1>value1</element1><element2>value2</element2></request>", request
23
+ end
24
+
25
+ def test_create_request__base_object_element
26
+ invoice = FreshBooks::Invoice.new
27
+ invoice.expects(:to_xml).with().returns("<invoice><number>23</number></invoice>")
28
+
29
+ request = @connection.send(:create_request, 'mymethod', 'invoice' => invoice)
30
+ assert_equal "<?xml version='1.0' encoding='UTF-8'?><request method='mymethod'><invoice><number>23</number></invoice></request>", request
31
+ end
32
+
33
+ def test_check_for_api_error__success
34
+ body = "body xml"
35
+ response = Net::HTTPSuccess.new("1.1", "200", "message")
36
+ response.expects(:body).with().returns(body)
37
+ assert_equal body, @connection.send(:check_for_api_error, response)
38
+ end
39
+
40
+ def test_check_for_api_error__unknown_system
41
+ response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
42
+ response.stubs(:[]).with("location").returns("loginSearch")
43
+ assert_raise(FreshBooks::UnknownSystemError) do
44
+ @connection.send(:check_for_api_error, response)
45
+ end
46
+ end
47
+
48
+ def test_check_for_api_error__deactivated
49
+ response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
50
+ response.stubs(:[]).with("location").returns("deactivated")
51
+ assert_raise(FreshBooks::AccountDeactivatedError) do
52
+ @connection.send(:check_for_api_error, response)
53
+ end
54
+ end
55
+
56
+ def test_check_for_api_error__unauthorized
57
+ response = Net::HTTPUnauthorized.new("1.1", "401", "message")
58
+ assert_raise(FreshBooks::AuthenticationError) do
59
+ @connection.send(:check_for_api_error, response)
60
+ end
61
+ end
62
+
63
+ def test_check_for_api_error__bad_request
64
+ response = Net::HTTPBadRequest.new("1.1", "401", "message")
65
+ assert_raise(FreshBooks::ApiAccessNotEnabledError) do
66
+ @connection.send(:check_for_api_error, response)
67
+ end
68
+ end
69
+
70
+ def test_check_for_api_error__internal_error
71
+ response = Net::HTTPBadGateway.new("1.1", "502", "message")
72
+ assert_raise(FreshBooks::InternalError) do
73
+ @connection.send(:check_for_api_error, response)
74
+ end
75
+
76
+ response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
77
+ response.stubs(:[]).with("location").returns("somePage")
78
+ assert_raise(FreshBooks::InternalError) do
79
+ @connection.send(:check_for_api_error, response)
80
+ end
81
+ end
82
+
83
+ def test_close_is_only_called_once_in_ntexted_start_sessions
84
+ @connection.expects(:obtain_connection)
85
+ @connection.expects(:close)
86
+
87
+ @connection.start_session { @connection.start_session { } }
88
+ end
89
+
90
+ def test_reconnect
91
+ connection = stub()
92
+
93
+ @connection.expects(:close).with()
94
+ @connection.expects(:obtain_connection).with(true).returns(connection)
95
+
96
+ assert_equal connection, @connection.send(:reconnect)
97
+ end
98
+
99
+ def test_post_request_successfull_request
100
+ request = "<request></request>"
101
+ response = "<response></response>"
102
+
103
+ http_connection = stub()
104
+ http_connection.expects(:request).with(request).returns(response)
105
+ @connection.expects(:start_session).with().yields(http_connection)
106
+
107
+ assert_equal response, @connection.send(:post_request, request)
108
+ end
109
+
110
+ def test_post_request_eof_error_retry
111
+ request = "<request></request>"
112
+ response = "<response></response>"
113
+ eof_error = EOFError.new("End of file error")
114
+
115
+ bad_http_connection = stub()
116
+ bad_http_connection.expects(:request).with(request).raises(eof_error)
117
+
118
+ new_http_connection = stub()
119
+ new_http_connection.expects(:request).with(request).returns(response)
120
+
121
+ @connection.expects(:start_session).with().yields(bad_http_connection)
122
+ @connection.expects(:reconnect).with().returns(new_http_connection)
123
+
124
+ assert_equal response, @connection.send(:post_request, request)
125
+ end
126
+
127
+ def test_post_request_eof_error_retry_only_retry_once
128
+ request = "<request></request>"
129
+ response = "<response></response>"
130
+ eof_error = EOFError.new("End of file error")
131
+
132
+ bad_http_connection = stub()
133
+ bad_http_connection.expects(:request).with(request).raises(eof_error)
134
+
135
+ new_http_connection = stub()
136
+ new_http_connection.expects(:request).with(request).raises(eof_error)
137
+
138
+ @connection.expects(:start_session).with().yields(bad_http_connection)
139
+ @connection.expects(:reconnect).with().returns(new_http_connection)
140
+
141
+ assert_raises(EOFError, eof_error.message) do
142
+ @connection.send(:post_request, request)
143
+ end
144
+ end
145
+ end