freshbooks.rb 3.0.18 → 3.0.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/History.txt +8 -0
  4. data/Manifest.txt +21 -1
  5. data/README.md +61 -0
  6. data/Rakefile +8 -13
  7. data/freshbooks.rb.gemspec +43 -0
  8. data/lib/freshbooks.rb +16 -2
  9. data/lib/freshbooks/address.rb +7 -0
  10. data/lib/freshbooks/api.rb +7 -0
  11. data/lib/freshbooks/autobill.rb +9 -0
  12. data/lib/freshbooks/base.rb +35 -9
  13. data/lib/freshbooks/budget.rb +7 -0
  14. data/lib/freshbooks/card.rb +9 -0
  15. data/lib/freshbooks/client.rb +3 -0
  16. data/lib/freshbooks/connection.rb +34 -25
  17. data/lib/freshbooks/contact.rb +9 -0
  18. data/lib/freshbooks/credits.rb +7 -0
  19. data/lib/freshbooks/estimate.rb +3 -3
  20. data/lib/freshbooks/expiration.rb +8 -0
  21. data/lib/freshbooks/gateway.rb +10 -0
  22. data/lib/freshbooks/gateway_transaction.rb +8 -0
  23. data/lib/freshbooks/invoice.rb +2 -2
  24. data/lib/freshbooks/language.rb +9 -0
  25. data/lib/freshbooks/line.rb +2 -0
  26. data/lib/freshbooks/links.rb +1 -1
  27. data/lib/freshbooks/payment.rb +2 -1
  28. data/lib/freshbooks/project.rb +4 -3
  29. data/lib/freshbooks/recurring.rb +5 -2
  30. data/lib/freshbooks/schema/definition.rb +2 -2
  31. data/lib/freshbooks/staff.rb +5 -0
  32. data/lib/freshbooks/system.rb +14 -0
  33. data/lib/freshbooks/tax.rb +12 -0
  34. data/lib/freshbooks/time_entry.rb +1 -0
  35. data/lib/freshbooks/xml_serializer.rb +5 -0
  36. data/lib/freshbooks/xml_serializer/serializers.rb +2 -2
  37. data/test/fixtures/freshbooks_credentials.yml +3 -0
  38. data/test/fixtures/recurring_create_response.xml +4 -0
  39. data/test/fixtures/recurring_get_response.xml +59 -0
  40. data/test/fixtures/recurring_list_response.xml +61 -0
  41. data/test/live_connection_test.rb +28 -0
  42. data/test/test_base.rb +59 -0
  43. data/test/test_connection.rb +39 -32
  44. data/test/test_helper.rb +20 -5
  45. data/test/test_invoice.rb +23 -37
  46. data/test/test_recurring.rb +98 -0
  47. metadata +67 -46
  48. data/README +0 -44
@@ -4,39 +4,39 @@ class TestConnection < Test::Unit::TestCase
4
4
  def setup
5
5
  @connection = FreshBooks::Connection.new("company.freshbooks.com", "auth_token")
6
6
  end
7
-
7
+
8
8
  def test_connection_accessors
9
9
  assert_equal "company.freshbooks.com", @connection.account_url
10
10
  assert_equal "auth_token", @connection.auth_token
11
11
  end
12
-
12
+
13
13
  def test_connection_request_headers
14
14
  request_headers = { "key" => "value" }
15
15
  connection = FreshBooks::Connection.new("company.freshbooks.com", "auth_token", request_headers)
16
16
  assert_equal request_headers, connection.request_headers
17
17
  end
18
-
19
-
18
+
19
+
20
20
  def test_create_request__array_of_elements
21
21
  request = @connection.send(:create_request, 'mymethod', [['element1', 'value1'], [:element2, :value2]])
22
22
  assert_equal "<?xml version='1.0' encoding='UTF-8'?><request method='mymethod'><element1>value1</element1><element2>value2</element2></request>", request
23
23
  end
24
-
24
+
25
25
  def test_create_request__base_object_element
26
26
  invoice = FreshBooks::Invoice.new
27
27
  invoice.expects(:to_xml).with().returns("<invoice><number>23</number></invoice>")
28
-
28
+
29
29
  request = @connection.send(:create_request, 'mymethod', 'invoice' => invoice)
30
30
  assert_equal "<?xml version='1.0' encoding='UTF-8'?><request method='mymethod'><invoice><number>23</number></invoice></request>", request
31
31
  end
32
-
32
+
33
33
  def test_check_for_api_error__success
34
34
  body = "body xml"
35
35
  response = Net::HTTPSuccess.new("1.1", "200", "message")
36
36
  response.expects(:body).with().returns(body)
37
37
  assert_equal body, @connection.send(:check_for_api_error, response)
38
38
  end
39
-
39
+
40
40
  def test_check_for_api_error__unknown_system
41
41
  response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
42
42
  response.stubs(:[]).with("location").returns("loginSearch")
@@ -44,7 +44,7 @@ class TestConnection < Test::Unit::TestCase
44
44
  @connection.send(:check_for_api_error, response)
45
45
  end
46
46
  end
47
-
47
+
48
48
  def test_check_for_api_error__deactivated
49
49
  response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
50
50
  response.stubs(:[]).with("location").returns("deactivated")
@@ -52,94 +52,101 @@ class TestConnection < Test::Unit::TestCase
52
52
  @connection.send(:check_for_api_error, response)
53
53
  end
54
54
  end
55
-
55
+
56
56
  def test_check_for_api_error__unauthorized
57
57
  response = Net::HTTPUnauthorized.new("1.1", "401", "message")
58
58
  assert_raise(FreshBooks::AuthenticationError) do
59
59
  @connection.send(:check_for_api_error, response)
60
60
  end
61
61
  end
62
-
62
+
63
63
  def test_check_for_api_error__bad_request
64
64
  response = Net::HTTPBadRequest.new("1.1", "401", "message")
65
65
  assert_raise(FreshBooks::ApiAccessNotEnabledError) do
66
66
  @connection.send(:check_for_api_error, response)
67
67
  end
68
68
  end
69
-
69
+
70
70
  def test_check_for_api_error__internal_error
71
71
  response = Net::HTTPBadGateway.new("1.1", "502", "message")
72
72
  assert_raise(FreshBooks::InternalError) do
73
73
  @connection.send(:check_for_api_error, response)
74
74
  end
75
-
75
+
76
76
  response = Net::HTTPMovedPermanently.new("1.1", "301", "message")
77
77
  response.stubs(:[]).with("location").returns("somePage")
78
78
  assert_raise(FreshBooks::InternalError) do
79
79
  @connection.send(:check_for_api_error, response)
80
80
  end
81
81
  end
82
-
82
+
83
83
  def test_close_is_only_called_once_in_ntexted_start_sessions
84
84
  @connection.expects(:obtain_connection)
85
85
  @connection.expects(:close)
86
-
86
+
87
87
  @connection.start_session { @connection.start_session { } }
88
88
  end
89
-
89
+
90
90
  def test_reconnect
91
91
  connection = stub()
92
-
92
+
93
93
  @connection.expects(:close).with()
94
94
  @connection.expects(:obtain_connection).with(true).returns(connection)
95
-
95
+
96
96
  assert_equal connection, @connection.send(:reconnect)
97
97
  end
98
-
98
+
99
99
  def test_post_request_successfull_request
100
100
  request = "<request></request>"
101
101
  response = "<response></response>"
102
-
102
+
103
103
  http_connection = stub()
104
104
  http_connection.expects(:request).with(request).returns(response)
105
105
  @connection.expects(:start_session).with().yields(http_connection)
106
-
106
+
107
107
  assert_equal response, @connection.send(:post_request, request)
108
108
  end
109
-
109
+
110
110
  def test_post_request_eof_error_retry
111
111
  request = "<request></request>"
112
112
  response = "<response></response>"
113
113
  eof_error = EOFError.new("End of file error")
114
-
114
+
115
115
  bad_http_connection = stub()
116
116
  bad_http_connection.expects(:request).with(request).raises(eof_error)
117
-
117
+
118
118
  new_http_connection = stub()
119
119
  new_http_connection.expects(:request).with(request).returns(response)
120
-
120
+
121
121
  @connection.expects(:start_session).with().yields(bad_http_connection)
122
122
  @connection.expects(:reconnect).with().returns(new_http_connection)
123
-
123
+
124
124
  assert_equal response, @connection.send(:post_request, request)
125
125
  end
126
-
126
+
127
127
  def test_post_request_eof_error_retry_only_retry_once
128
128
  request = "<request></request>"
129
129
  response = "<response></response>"
130
130
  eof_error = EOFError.new("End of file error")
131
-
131
+
132
132
  bad_http_connection = stub()
133
133
  bad_http_connection.expects(:request).with(request).raises(eof_error)
134
-
134
+
135
135
  new_http_connection = stub()
136
136
  new_http_connection.expects(:request).with(request).raises(eof_error)
137
-
137
+
138
138
  @connection.expects(:start_session).with().yields(bad_http_connection)
139
139
  @connection.expects(:reconnect).with().returns(new_http_connection)
140
-
141
- assert_raises(EOFError, eof_error.message) do
140
+
141
+ assert_raises(EOFError, eof_error.message) do
142
142
  @connection.send(:post_request, request)
143
143
  end
144
144
  end
145
+
146
+ def test_format_of_account_url_establish_connection
147
+ exception = assert_raise(FreshBooks::InvalidAccountUrlError) do
148
+ FreshBooks::Connection.new("https://company.freshbooks.com", "auth_token")
149
+ end
150
+ assert_equal "account_url is expected to be in the form www.example.com without any protocol string or trailing query parameters", exception.message
151
+ end
145
152
  end
@@ -3,6 +3,7 @@ require File.dirname(__FILE__) + '/../lib/freshbooks'
3
3
  require 'stringio'
4
4
  require 'test/unit'
5
5
  require File.dirname(__FILE__) + '/mock_connection'
6
+ require 'active_support/all'
6
7
 
7
8
  begin
8
9
  require 'mocha'
@@ -13,7 +14,7 @@ rescue LoadError
13
14
  end
14
15
 
15
16
  class Test::Unit::TestCase
16
-
17
+
17
18
  @@fixtures = {}
18
19
  def self.fixtures list
19
20
  [list].flatten.each do |fixture|
@@ -27,22 +28,36 @@ class Test::Unit::TestCase
27
28
  end
28
29
  end
29
30
  end
30
-
31
-
31
+
32
+
32
33
  def mock_connection(file_name)
33
34
  mock_connection = MockConnection.new(fixture_xml_content(file_name))
34
35
  FreshBooks::Base.stubs(:connection).with().returns(mock_connection)
35
36
  mock_connection
36
37
  end
37
-
38
+
38
39
  def fixture_xml_content(file_name)
39
40
  # Quick way to remove white space and newlines from xml. Makes it easier to compare in tests
40
41
  open(File.join(fixture_dir, "#{file_name}.xml"), "r").readlines.inject("") do |contents, line|
41
42
  contents + line.strip
42
43
  end
43
44
  end
44
-
45
+
45
46
  def fixture_dir
46
47
  File.join(File.dirname(__FILE__), "fixtures")
47
48
  end
49
+
50
+ def assert_line(line, number)
51
+ number = number + 1
52
+
53
+ assert_equal number.to_f, line.amount
54
+ assert_equal "name#{number}", line.name
55
+ assert_equal "description#{number}", line.description
56
+ assert_equal number.to_f, line.unit_cost
57
+ assert_equal number, line.quantity
58
+ assert_equal "tax1_name#{number}", line.tax1_name
59
+ assert_equal "tax2_name#{number}", line.tax2_name
60
+ assert_equal number.to_f, line.tax1_percent
61
+ assert_equal number.to_f, line.tax2_percent
62
+ end
48
63
  end
@@ -3,74 +3,74 @@ require File.dirname(__FILE__) + '/test_helper.rb'
3
3
  class TestInvoice < Test::Unit::TestCase
4
4
  def test_list
5
5
  mock_call_api("invoice.list", { "page" => 1 }, "invoice_list_response")
6
-
6
+
7
7
  invoices = FreshBooks::Invoice.list
8
8
  assert_equal 3, invoices.size
9
9
  assert_invoice invoices[0], 0
10
10
  assert_invoice invoices[1], 1
11
11
  end
12
-
12
+
13
13
  def test_get
14
14
  invoice_id = 2
15
15
  mock_call_api("invoice.get", { "invoice_id" => invoice_id }, "invoice_get_response")
16
-
16
+
17
17
  invoice = FreshBooks::Invoice.get(invoice_id)
18
18
  assert_invoice invoice, 0, true
19
19
  end
20
-
20
+
21
21
  def test_create
22
22
  invoice = FreshBooks::Invoice.new
23
23
  assert_nil invoice.invoice_id
24
-
24
+
25
25
  mock_call_api("invoice.create", { "invoice" => invoice }, "invoice_create_response")
26
26
  assert invoice.create
27
27
  assert_equal 1, invoice.invoice_id
28
28
  end
29
-
29
+
30
30
  def test_update
31
31
  invoice = FreshBooks::Invoice.new
32
32
  invoice.invoice_id = 1
33
-
33
+
34
34
  mock_call_api("invoice.update", { "invoice" => invoice }, "success_response")
35
35
  assert invoice.update
36
36
  end
37
-
37
+
38
38
  def test_delete
39
39
  invoice = FreshBooks::Invoice.new
40
40
  invoice.invoice_id = 2
41
41
  mock_call_api("invoice.delete", { "invoice_id" => invoice.invoice_id }, "success_response")
42
-
42
+
43
43
  assert invoice.delete
44
44
  end
45
-
45
+
46
46
  def test_send_by_email
47
47
  invoice = FreshBooks::Invoice.new
48
48
  invoice.invoice_id = 2
49
49
  mock_call_api("invoice.sendByEmail", { "invoice_id" => invoice.invoice_id }, "success_response")
50
-
50
+
51
51
  assert invoice.send_by_email
52
52
  end
53
-
53
+
54
54
  def test_send_by_snail_mail
55
55
  invoice = FreshBooks::Invoice.new
56
56
  invoice.invoice_id = 2
57
57
  mock_call_api("invoice.sendBySnailMail", { "invoice_id" => invoice.invoice_id }, "success_response")
58
-
58
+
59
59
  assert invoice.send_by_snail_mail
60
60
  end
61
-
61
+
62
62
  private
63
-
63
+
64
64
  def mock_call_api(method, options, response_fixture)
65
65
  FreshBooks::Base.connection.
66
66
  expects(:call_api).
67
67
  with(method, options).
68
68
  returns(FreshBooks::Response.new(fixture_xml_content(response_fixture)))
69
69
  end
70
-
70
+
71
71
  def assert_invoice(invoice, number, expanded_form = false)
72
72
  number = number + 1
73
-
73
+
74
74
  assert_equal number, invoice.invoice_id
75
75
  assert_equal "number#{number}", invoice.number
76
76
  assert_equal number, invoice.client_id
@@ -79,7 +79,7 @@ private
79
79
  assert_equal (number * 100), invoice.amount
80
80
  assert_equal (number * 100) / 2, invoice.amount_outstanding
81
81
  assert_equal Date.parse("2009-02-0#{number}"), invoice.date
82
-
82
+
83
83
  assert_equal number, invoice.po_number
84
84
  assert_equal number.to_f, invoice.discount
85
85
  assert_equal "notes#{number}", invoice.notes
@@ -92,34 +92,20 @@ private
92
92
  assert_equal "p_state#{number}", invoice.p_state
93
93
  assert_equal "p_country#{number}", invoice.p_country
94
94
  assert_equal "p_code#{number}", invoice.p_code
95
-
95
+
96
96
  assert_equal "return_uri#{number}", invoice.return_uri
97
97
  assert_equal DateTime.parse("2009-08-#{number} 0#{number}:00:00 -04:00"), invoice.updated
98
-
99
-
100
-
98
+
99
+
100
+
101
101
  assert_equal "client_view#{number}", invoice.links.client_view
102
102
  assert_equal "view#{number}", invoice.links.view
103
103
  assert_equal "edit#{number}", invoice.links.edit
104
-
104
+
105
105
  lines = invoice.lines
106
106
  assert_equal 1, lines.size
107
107
  lines.each_with_index do |line, index|
108
108
  assert_line(line, index)
109
109
  end
110
110
  end
111
-
112
- def assert_line(line, number)
113
- number = number + 1
114
-
115
- assert_equal number.to_f, line.amount
116
- assert_equal "name#{number}", line.name
117
- assert_equal "description#{number}", line.description
118
- assert_equal number.to_f, line.unit_cost
119
- assert_equal number, line.quantity
120
- assert_equal "tax1_name#{number}", line.tax1_name
121
- assert_equal "tax2_name#{number}", line.tax2_name
122
- assert_equal number.to_f, line.tax1_percent
123
- assert_equal number.to_f, line.tax2_percent
124
- end
125
111
  end
@@ -0,0 +1,98 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestRecurring < Test::Unit::TestCase
4
+ def test_list
5
+ mock_call_api("recurring.list", { "page" => 1 }, "recurring_list_response")
6
+
7
+ recurrings = FreshBooks::Recurring.list
8
+ assert_equal 1, recurrings.size
9
+ assert_recurring recurrings[0], 0
10
+ end
11
+
12
+ def test_get
13
+ recurring_id = 2
14
+ mock_call_api("recurring.get", { "recurring_id" => recurring_id }, "recurring_get_response")
15
+
16
+ recurring = FreshBooks::Recurring.get(recurring_id)
17
+ assert_recurring recurring, 0, true
18
+ end
19
+
20
+ def test_create
21
+ recurring = FreshBooks::Recurring.new
22
+ assert_nil recurring.recurring_id
23
+
24
+ mock_call_api("recurring.create", { "recurring" => recurring }, "recurring_create_response")
25
+ assert recurring.create
26
+ assert_equal 1, recurring.recurring_id
27
+ end
28
+
29
+ def test_update
30
+ recurring = FreshBooks::Recurring.new
31
+ recurring.recurring_id = 1
32
+
33
+ mock_call_api("recurring.update", { "recurring" => recurring }, "success_response")
34
+ assert recurring.update
35
+ end
36
+
37
+ def test_delete
38
+ recurring = FreshBooks::Recurring.new
39
+ recurring.recurring_id = 2
40
+ mock_call_api("recurring.delete", { "recurring_id" => recurring.recurring_id }, "success_response")
41
+
42
+ assert recurring.delete
43
+ end
44
+
45
+ private
46
+
47
+ def mock_call_api(method, options, response_fixture)
48
+ FreshBooks::Base.connection.
49
+ expects(:call_api).
50
+ with(method, options).
51
+ returns(FreshBooks::Response.new(fixture_xml_content(response_fixture)))
52
+ end
53
+
54
+ def assert_recurring(recurring, number, expanded_form = false)
55
+ number = number + 1
56
+
57
+ assert_equal number, recurring.recurring_id
58
+
59
+ assert_equal 'm', recurring.frequency
60
+ assert_equal number, recurring.occurrences
61
+ assert_equal false, recurring.stopped
62
+
63
+ assert_equal number, recurring.client_id
64
+ assert_equal (number * 100), recurring.amount
65
+ assert_equal Date.parse("2011-04-0#{number}"), recurring.date
66
+
67
+ assert_equal number, recurring.po_number
68
+ assert_equal number.to_f, recurring.discount
69
+ assert_equal "notes#{number}", recurring.notes
70
+ assert_equal "terms#{number}", recurring.terms
71
+
72
+ assert_equal "first_name#{number}", recurring.first_name
73
+ assert_equal "last_name#{number}", recurring.last_name
74
+ assert_equal "organization#{number}", recurring.organization
75
+ assert_equal "p_street1#{number}", recurring.p_street1
76
+ assert_equal "p_street2#{number}", recurring.p_street2
77
+ assert_equal "p_city#{number}", recurring.p_city
78
+ assert_equal "p_state#{number}", recurring.p_state
79
+ assert_equal "p_country#{number}", recurring.p_country
80
+ assert_equal "p_code#{number}", recurring.p_code
81
+
82
+ assert_equal "return_uri#{number}", recurring.return_uri
83
+ assert_equal true, recurring.send_email
84
+ assert_equal false, recurring.send_snail_mail
85
+
86
+ assert_autobill recurring.autobill, number
87
+
88
+ lines = recurring.lines
89
+ assert_equal 1, lines.size
90
+ lines.each_with_index do |line, index|
91
+ assert_line(line, index)
92
+ end
93
+ end
94
+
95
+ def assert_autobill(autobill, number)
96
+ assert "gateway_name#{number}", autobill.gateway_name
97
+ end
98
+ end