freeagent 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/CHANGELOG.rdoc +17 -0
  2. data/Gemfile.lock +12 -10
  3. data/README.rdoc +13 -1
  4. data/TODO +22 -0
  5. data/freeagent.gemspec +4 -3
  6. data/lib/free_agent.rb +1 -0
  7. data/lib/free_agent/base.rb +132 -0
  8. data/lib/free_agent/contact.rb +68 -0
  9. data/lib/free_agent/invoice.rb +58 -0
  10. data/lib/free_agent/project.rb +20 -0
  11. data/lib/free_agent/task.rb +10 -0
  12. data/lib/free_agent/version.rb +1 -1
  13. data/spec/fixtures/bank_accounts/all.xml +24 -19
  14. data/spec/fixtures/bank_accounts/single.xml +6 -6
  15. data/spec/fixtures/contacts/all.xml +13 -13
  16. data/spec/fixtures/contacts/all_filter_all.xml +69 -0
  17. data/spec/fixtures/contacts/invoices.xml +75 -0
  18. data/spec/fixtures/contacts/single.xml +6 -6
  19. data/spec/fixtures/estimates/all.xml +38 -0
  20. data/spec/fixtures/expenses/all.xml +93 -0
  21. data/spec/fixtures/expenses/single.xml +31 -0
  22. data/spec/fixtures/expenses/single_with_project_id.xml +31 -0
  23. data/spec/fixtures/invoices/all.xml +112 -75
  24. data/spec/fixtures/invoices/single.xml +38 -37
  25. data/spec/fixtures/projects/invoices.xml +39 -0
  26. data/spec/fixtures/projects/single.xml +2 -2
  27. data/spec/fixtures/projects/tasks.xml +30 -0
  28. data/spec/fixtures/projects/timeslips.xml +25 -0
  29. data/spec/fixtures/tasks/single.xml +10 -0
  30. data/spec/fixtures/timeslips/all.xml +25 -0
  31. data/spec/fixtures/timeslips/single.xml +12 -0
  32. data/spec/spec_helper.rb +1 -1
  33. data/spec/support/helper.rb +4 -4
  34. data/spec/support/mimic.rb +40 -25
  35. data/spec/unit/bank_account_spec.rb +1 -1
  36. data/spec/unit/base_spec.rb +113 -0
  37. data/spec/unit/contact_spec.rb +87 -1
  38. data/spec/unit/invoice_item_spec.rb +1 -1
  39. data/spec/unit/invoice_spec.rb +48 -2
  40. data/spec/unit/project_spec.rb +34 -0
  41. data/spec/unit/task_spec.rb +77 -0
  42. metadata +45 -7
@@ -0,0 +1,25 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <timeslips type="array">
3
+ <timeslip>
4
+ <id type="integer">5903024</id>
5
+ <dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
6
+ <hours type="decimal">1.0</hours>
7
+ <comment>I did something.</comment>
8
+ <user-id type="integer">82948</user-id>
9
+ <project-id type="integer">116526</project-id>
10
+ <task-id type="integer">186247</task-id>
11
+ <updated-at type="integer">Tue Apr 12 20:48:43 UTC 2011</updated-at>
12
+ <status></status>
13
+ </timeslip>
14
+ <timeslip>
15
+ <id type="integer">5903025</id>
16
+ <dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
17
+ <hours type="decimal">3.0</hours>
18
+ <comment>It was a very difficult task!</comment>
19
+ <user-id type="integer">82948</user-id>
20
+ <project-id type="integer">116526</project-id>
21
+ <task-id type="integer">186247</task-id>
22
+ <updated-at type="integer">Tue Apr 12 20:49:21 UTC 2011</updated-at>
23
+ <status></status>
24
+ </timeslip>
25
+ </timeslips>
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <timeslip>
3
+ <id type="integer">5903024</id>
4
+ <dated-on type="datetime">2011-04-12T00:00:00+00:00</dated-on>
5
+ <hours type="decimal">1.0</hours>
6
+ <comment>I did something.</comment>
7
+ <user-id type="integer">82948</user-id>
8
+ <project-id type="integer">116526</project-id>
9
+ <task-id type="integer">186247</task-id>
10
+ <updated-at type="integer">Tue Apr 12 20:48:43 UTC 2011</updated-at>
11
+ <status></status>
12
+ </timeslip>
@@ -19,7 +19,7 @@ end
19
19
  Dir[File.join(SPEC_ROOT, "support/**/*.rb")].each { |f| require f }
20
20
 
21
21
  RSpec.configure do |config|
22
- # config.mock_with :rr
22
+ config.mock_with :rr
23
23
 
24
24
  config.before(:each) do
25
25
  FreeAgent.configure do |config|
@@ -7,12 +7,12 @@ module Helper
7
7
  described_class
8
8
  end
9
9
 
10
- def fixture(*name)
11
- File.join(SPEC_ROOT, "fixtures", *name)
12
- end
13
-
14
10
  end
15
11
 
16
12
  RSpec.configure do |config|
17
13
  config.include Helper
18
14
  end
15
+
16
+ def fixture(*path)
17
+ File.read(File.join(SPEC_ROOT, 'fixtures', *path))
18
+ end
@@ -1,9 +1,5 @@
1
1
  require 'mimic'
2
2
 
3
- def fixture(*path)
4
- File.read(File.join(SPEC_ROOT, 'fixtures', *path))
5
- end
6
-
7
3
  Mimic.mimic do
8
4
 
9
5
  use Rack::Auth::Basic do |username, password|
@@ -14,9 +10,9 @@ Mimic.mimic do
14
10
  get('/attachments/1.xml').returning "", 404
15
11
  get('/attachments/2.xml').returning fixture('attachments/single.xml')
16
12
 
17
- get('/bank_accounts.xml').returning fixture('bank_accounts/all.xml')
18
- get('/bank_accounts/1.xml').returning "", 404
19
- get('/bank_accounts/2.xml').returning fixture('bank_accounts/single.xml')
13
+ get('/bank_accounts.xml').returning fixture('bank_accounts/all.xml')
14
+ get('/bank_accounts/1.xml').returning "", 404
15
+ get('/bank_accounts/84192.xml').returning fixture('bank_accounts/single.xml')
20
16
 
21
17
  get('/bills.xml') do
22
18
  return [400, {}, []] unless params[:period]
@@ -26,22 +22,41 @@ Mimic.mimic do
26
22
  get('/bills/1.xml').returning "", 404
27
23
  get('/bills/2.xml').returning fixture('bills/single.xml')
28
24
 
29
- get('/contacts.xml').returning fixture('contacts/all.xml')
30
- get('/contacts/1.xml').returning "", 404
31
- get('/contacts/2.xml').returning fixture('contacts/single.xml')
32
-
33
- get('/invoices.xml').returning fixture('invoices/all.xml')
34
- get('/invoices/1.xml').returning "", 404
35
- get('/invoices/2.xml').returning fixture('invoices/single.xml')
36
-
37
- get('/invoices/1/*') do
38
- [404, {}, []]
39
- end
40
- get('/invoices/2/invoice_items.xml').returning fixture('invoice_items/all.xml')
41
- get('/invoices/2/invoice_items/1.xml').returning "", 404
42
- get('/invoices/2/invoice_items/2.xml').returning fixture('invoice_items/single.xml')
43
-
44
- get('/projects.xml').returning fixture('projects/all.xml')
45
- get('/projects/1.xml').returning "", 404
46
- get('/projects/2.xml').returning fixture('projects/single.xml')
25
+ get('/contacts.xml').returning fixture('contacts/all.xml')
26
+ get('/contacts/1.xml').returning "", 404
27
+ get('/contacts/447693.xml').returning fixture('contacts/single.xml')
28
+ get('/contacts/1/invoices.xml').returning "", 404
29
+ get('/contacts/469012/invoices.xml').returning fixture('contacts/invoices.xml')
30
+
31
+ get('/invoices.xml').
32
+ returning fixture('invoices/all.xml')
33
+ get('/invoices/1.xml').
34
+ returning "", 404
35
+ get('/invoices/1/*').
36
+ returning "", 404
37
+ get('/invoices/2715138.xml').
38
+ returning fixture('invoices/single.xml')
39
+
40
+ get('/invoices/2/invoice_items.xml').
41
+ returning fixture('invoice_items/all.xml')
42
+ get('/invoices/2/invoice_items/1.xml').
43
+ returning "", 404
44
+ get('/invoices/2/invoice_items/2.xml').
45
+ returning fixture('invoice_items/single.xml')
46
+
47
+ put('/invoices/2715138/mark_as_draft.xml').
48
+ returning "", 200
49
+ put('/invoices/2715138/mark_as_sent.xml').
50
+ returning "", 200
51
+ put('/invoices/2715138/mark_as_cancelled.xml').
52
+ returning "", 200
53
+
54
+ get('/projects.xml').returning fixture('projects/all.xml')
55
+ get('/projects/1.xml').returning "", 404
56
+ get('/projects/1/*').returning "", 404
57
+ get('/projects/2.xml').returning fixture('projects/single.xml')
58
+ get('/projects/2/invoices.xml').returning fixture('projects/invoices.xml')
59
+ get('/projects/2/tasks.xml').returning fixture('projects/tasks.xml')
60
+ get('/projects/2/tasks/1.xml').returning "", 404
61
+ get('/projects/2/tasks/2.xml').returning fixture('tasks/single.xml')
47
62
  end
@@ -36,7 +36,7 @@ describe FreeAgent::BankAccount do
36
36
  describe ".find(id)" do
37
37
  context "when the record exists" do
38
38
  before(:each) do
39
- @bank_account = FreeAgent::BankAccount.find(2)
39
+ @bank_account = FreeAgent::BankAccount.find(84192)
40
40
  end
41
41
 
42
42
  it "returns a bank account" do
@@ -6,4 +6,117 @@ describe FreeAgent::Base do
6
6
  klass.superclass.should == ActiveResource::Base
7
7
  end
8
8
 
9
+
10
+ describe "DynamicFinderMatch" do
11
+
12
+ class Record < FreeAgent::Base
13
+ end
14
+
15
+ before(:each) do
16
+ @records = [
17
+ Record.new(:username => "alpha", :email => "alpha@example.org", :locale => "en"),
18
+ Record.new(:username => "beta", :email => "beta@example.org", :locale => "en"),
19
+ Record.new(:username => "charlie", :email => "charlie@example.org", :locale => "jp"),
20
+ Record.new(:username => "delta", :email => "delta@example.org", :locale => "en"),
21
+ Record.new(:username => "beta", :email => "beta@example.org", :locale => "jp"),
22
+ ]
23
+ stub(Record).all { @records }
24
+ end
25
+
26
+ describe "#find_by_" do
27
+ it "fetches .all records" do
28
+ mock(Record).all { @records }
29
+ Record.find_by_username('test')
30
+ end
31
+
32
+ it "searches and returns the first item matching criteria" do
33
+ Record.find_by_locale('en').should == @records[0]
34
+ end
35
+
36
+ it "supports multiple criteria" do
37
+ Record.find_by_email_and_username('beta@example.org', 'beta').should == @records[1]
38
+ Record.find_by_username_and_email('beta', 'beta@example.org').should == @records[1]
39
+ end
40
+ end
41
+
42
+ describe "#find_or_initialize_by_" do
43
+ it "fetches .all records" do
44
+ mock(Record).all { @records }
45
+ Record.find_or_initialize_by_username('test')
46
+ end
47
+
48
+ it "searches and returns the first item matching criteria" do
49
+ Record.find_or_initialize_by_username('en').should == @records[0]
50
+ end
51
+
52
+ it "supports multiple criteria" do
53
+ Record.find_or_initialize_by_email_and_username('beta@example.org', 'beta').should == @records[1]
54
+ Record.find_or_initialize_by_username_and_email('beta', 'beta@example.org').should == @records[1]
55
+ end
56
+
57
+ it "initializes the record if not available" do
58
+ record = Record.find_or_initialize_by_email('hello@example.org', 'hello')
59
+ record.email.should == 'hello@example.org'
60
+
61
+ record = Record.find_or_initialize_by_username_and_email('hello', 'hello@example.org')
62
+ record.username.should == 'hello'
63
+ record.email.should == 'hello@example.org'
64
+ end
65
+ end
66
+
67
+ describe "#find_all_by_" do
68
+ it "fetches .all records" do
69
+ mock(Record).all { @records }
70
+ Record.find_all_by_username('test')
71
+ end
72
+
73
+ it "searches and returns all items matching criteria" do
74
+ Record.find_all_by_locale('en').should == [@records[0], @records[1], @records[3]]
75
+ end
76
+
77
+ it "supports multiple criteria" do
78
+ Record.find_all_by_email_and_username('beta@example.org', 'beta').should == [@records[1], @records[4]]
79
+ Record.find_all_by_username_and_email('beta', 'beta@example.org').should == [@records[1], @records[4]]
80
+ end
81
+ end
82
+
83
+ describe "#find_last_by_" do
84
+ it "fetches .all records" do
85
+ mock(Record).all { @records }
86
+ Record.find_last_by_username('test')
87
+ end
88
+
89
+ it "searches and returns the last item matching criteria" do
90
+ Record.find_last_by_locale('en').should == @records[3]
91
+ end
92
+
93
+ it "supports multiple criteria" do
94
+ Record.find_last_by_email_and_username('beta@example.org', 'beta').should == @records[4]
95
+ Record.find_last_by_username_and_email('beta', 'beta@example.org').should == @records[4]
96
+ end
97
+ end
98
+
99
+ describe "#respond_to?" do
100
+ it "responds to find_by_*" do
101
+ Record.new.should respond_to(:find_by_name)
102
+ Record.new.should respond_to(:find_by_name_and_email)
103
+ end
104
+
105
+ it "responds to find_or_initialize_by_*" do
106
+ Record.new.should respond_to(:find_or_initialize_by_name)
107
+ Record.new.should respond_to(:find_or_initialize_by_name_and_email)
108
+ end
109
+
110
+ it "responds to find_all_by_*" do
111
+ Record.new.should respond_to(:find_all_by_name)
112
+ Record.new.should respond_to(:find_all_by_name_and_email)
113
+ end
114
+
115
+ it "responds to find_last_by_*" do
116
+ Record.new.should respond_to(:find_last_by_name)
117
+ Record.new.should respond_to(:find_last_by_name_and_email)
118
+ end
119
+ end
120
+ end
121
+
9
122
  end
@@ -18,6 +18,35 @@ describe FreeAgent::Contact do
18
18
  end
19
19
 
20
20
 
21
+ describe "validations" do
22
+ it "requires either name or organisation_name" do
23
+ c = klass.new
24
+ c.should_not be_valid
25
+ c.errors[:first_name].should_not be_nil
26
+ c.errors[:last_name].should_not be_nil
27
+ c.errors[:organisation_name].should_not be_nil
28
+
29
+ c = klass.new(:last_name => "Carletti")
30
+ c.should_not be_valid
31
+ c.errors[:first_name].should_not be_nil
32
+
33
+ c = klass.new(:first_name => "Simone")
34
+ c.should_not be_valid
35
+ c.errors[:last_name].should_not be_nil
36
+ end
37
+
38
+ it "is valid with name" do
39
+ c = klass.new(:first_name => "Simone", :last_name => "Carletti")
40
+ c.should be_valid
41
+ end
42
+
43
+ it "is valid with organisation_name" do
44
+ c = klass.new(:organisation_name => "Company")
45
+ c.should be_valid
46
+ end
47
+ end
48
+
49
+
21
50
  describe ".all" do
22
51
  before(:each) do
23
52
  @contacts = FreeAgent::Contact.all
@@ -36,7 +65,7 @@ describe FreeAgent::Contact do
36
65
  describe ".find(id)" do
37
66
  context "when the record exists" do
38
67
  before(:each) do
39
- @contacts = FreeAgent::Contact.find(2)
68
+ @contacts = FreeAgent::Contact.find(447693)
40
69
  end
41
70
 
42
71
  it "returns a contact" do
@@ -53,4 +82,61 @@ describe FreeAgent::Contact do
53
82
  end
54
83
  end
55
84
 
85
+
86
+ describe "#name" do
87
+ it "returns nil when no first_name and last_name" do
88
+ klass.new.name.should be_nil
89
+ end
90
+
91
+ it "returns the first_name when first_name" do
92
+ klass.new(:last_name => "Carletti").
93
+ name.should == "Carletti"
94
+ end
95
+
96
+ it "returns the first_name when first_name" do
97
+ klass.new(:first_name => "Simone").
98
+ name.should == "Simone"
99
+ end
100
+
101
+ it "returns first_name and last name" do
102
+ klass.new(:first_name => "Simone", :last_name => "Carletti").
103
+ name.should == "Simone Carletti"
104
+ end
105
+ end
106
+
107
+ describe "#invoices" do
108
+ it "merges the finder options" do
109
+ mock(FreeAgent::Invoice).all(:from => '/contacts/0/invoices.xml')
110
+ FreeAgent::Contact.new(:id => 0).invoices
111
+
112
+ mock(FreeAgent::Invoice).all(:from => '/contacts/0/invoices.xml', :params => { :foo => 'bar' })
113
+ FreeAgent::Contact.new(:id => 0).invoices(:params => { :foo => 'bar' })
114
+ end
115
+
116
+ context "when the contact exists" do
117
+ before(:each) do
118
+ @invoices = FreeAgent::Contact.new(:id => 469012).invoices
119
+ end
120
+
121
+ it "returns an array" do
122
+ @invoices.should be_a(Array)
123
+ end
124
+
125
+ it "returns the invoices" do
126
+ @invoices.should have(2).records
127
+ @invoices.first.should be_a(FreeAgent::Invoice)
128
+ @invoices.first.id.should == 2715138
129
+ end
130
+ end
131
+
132
+ context "when the contact does not exist" do
133
+ it "raises a ResourceNotFound error" do
134
+ pending 'ActiveResource rescues the error and returns nil'
135
+ lambda do
136
+ FreeAgent::Contact.new(:id => 1).invoices
137
+ end.should raise_error(ActiveResource::ResourceNotFound)
138
+ end
139
+ end
140
+ end
141
+
56
142
  end
@@ -58,7 +58,7 @@ describe FreeAgent::InvoiceItem do
58
58
  context "when the invoice does not exist" do
59
59
  describe ".all" do
60
60
  it "raises a ResourceNotFound error" do
61
- pending 'ActiveResource returns nil'
61
+ pending 'ActiveResource rescues the error and returns nil'
62
62
  lambda do
63
63
  FreeAgent::InvoiceItem.all(:params => { :invoice_id => 1 })
64
64
  end.should raise_error(ActiveResource::ResourceNotFound)
@@ -17,6 +17,16 @@ describe FreeAgent::Invoice do
17
17
  end
18
18
  end
19
19
 
20
+ describe "initialization" do
21
+ context "from XML" do
22
+ it "parses the #invoice_items attribute into an array of InvoiceItem" do
23
+ @invoice = FreeAgent::Invoice.find(2715138)
24
+ @invoice.invoice_items.should be_a(Array)
25
+ @invoice.invoice_items.first.should be_a(FreeAgent::InvoiceItem)
26
+ end
27
+ end
28
+ end
29
+
20
30
 
21
31
  describe ".all" do
22
32
  before(:each) do
@@ -28,7 +38,7 @@ describe FreeAgent::Invoice do
28
38
  end
29
39
 
30
40
  it "returns the invoices" do
31
- @invoices.should have(2).records
41
+ @invoices.should have(3).records
32
42
  @invoices.first.should be_a(FreeAgent::Invoice)
33
43
  end
34
44
  end
@@ -36,7 +46,7 @@ describe FreeAgent::Invoice do
36
46
  describe ".find(id)" do
37
47
  context "when the record exists" do
38
48
  before(:each) do
39
- @invoice = FreeAgent::Invoice.find(2)
49
+ @invoice = FreeAgent::Invoice.find(2715138)
40
50
  end
41
51
 
42
52
  it "returns an invoice" do
@@ -53,4 +63,40 @@ describe FreeAgent::Invoice do
53
63
  end
54
64
  end
55
65
 
66
+ describe "#mark_as_draft" do
67
+ before(:each) do
68
+ @invoice = FreeAgent::Invoice.find(2715138)
69
+ end
70
+
71
+ it "does not complain on success" do
72
+ @invoice.mark_as_draft.should be_true
73
+ end
74
+
75
+ it "reloads the attributes"
76
+ end
77
+
78
+ describe "#mark_as_sent" do
79
+ before(:each) do
80
+ @invoice = FreeAgent::Invoice.find(2715138)
81
+ end
82
+
83
+ it "does not complain on success" do
84
+ @invoice.mark_as_sent.should be_true
85
+ end
86
+
87
+ it "reloads the attributes"
88
+ end
89
+
90
+ describe "#mark_as_cancelled" do
91
+ before(:each) do
92
+ @invoice = FreeAgent::Invoice.find(2715138)
93
+ end
94
+
95
+ it "does not complain on success" do
96
+ @invoice.mark_as_cancelled.should be_true
97
+ end
98
+
99
+ it "reloads the attributes"
100
+ end
101
+
56
102
  end