freshbooks.rb 3.0.18 → 3.0.25

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 (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