freeagent 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +17 -0
- data/Gemfile.lock +12 -10
- data/README.rdoc +13 -1
- data/TODO +22 -0
- data/freeagent.gemspec +4 -3
- data/lib/free_agent.rb +1 -0
- data/lib/free_agent/base.rb +132 -0
- data/lib/free_agent/contact.rb +68 -0
- data/lib/free_agent/invoice.rb +58 -0
- data/lib/free_agent/project.rb +20 -0
- data/lib/free_agent/task.rb +10 -0
- data/lib/free_agent/version.rb +1 -1
- data/spec/fixtures/bank_accounts/all.xml +24 -19
- data/spec/fixtures/bank_accounts/single.xml +6 -6
- data/spec/fixtures/contacts/all.xml +13 -13
- data/spec/fixtures/contacts/all_filter_all.xml +69 -0
- data/spec/fixtures/contacts/invoices.xml +75 -0
- data/spec/fixtures/contacts/single.xml +6 -6
- data/spec/fixtures/estimates/all.xml +38 -0
- data/spec/fixtures/expenses/all.xml +93 -0
- data/spec/fixtures/expenses/single.xml +31 -0
- data/spec/fixtures/expenses/single_with_project_id.xml +31 -0
- data/spec/fixtures/invoices/all.xml +112 -75
- data/spec/fixtures/invoices/single.xml +38 -37
- data/spec/fixtures/projects/invoices.xml +39 -0
- data/spec/fixtures/projects/single.xml +2 -2
- data/spec/fixtures/projects/tasks.xml +30 -0
- data/spec/fixtures/projects/timeslips.xml +25 -0
- data/spec/fixtures/tasks/single.xml +10 -0
- data/spec/fixtures/timeslips/all.xml +25 -0
- data/spec/fixtures/timeslips/single.xml +12 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/helper.rb +4 -4
- data/spec/support/mimic.rb +40 -25
- data/spec/unit/bank_account_spec.rb +1 -1
- data/spec/unit/base_spec.rb +113 -0
- data/spec/unit/contact_spec.rb +87 -1
- data/spec/unit/invoice_item_spec.rb +1 -1
- data/spec/unit/invoice_spec.rb +48 -2
- data/spec/unit/project_spec.rb +34 -0
- data/spec/unit/task_spec.rb +77 -0
- 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>
|
data/spec/spec_helper.rb
CHANGED
data/spec/support/helper.rb
CHANGED
@@ -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
|
data/spec/support/mimic.rb
CHANGED
@@ -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
|
18
|
-
get('/bank_accounts/1.xml').returning
|
19
|
-
get('/bank_accounts/
|
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
|
30
|
-
get('/contacts/1.xml').returning
|
31
|
-
get('/contacts/
|
32
|
-
|
33
|
-
get('/invoices.xml').returning
|
34
|
-
|
35
|
-
get('/invoices
|
36
|
-
|
37
|
-
get('/invoices/1
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
get('/invoices/
|
42
|
-
|
43
|
-
|
44
|
-
get('/
|
45
|
-
|
46
|
-
get('/
|
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(
|
39
|
+
@bank_account = FreeAgent::BankAccount.find(84192)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "returns a bank account" do
|
data/spec/unit/base_spec.rb
CHANGED
@@ -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
|
data/spec/unit/contact_spec.rb
CHANGED
@@ -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(
|
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)
|
data/spec/unit/invoice_spec.rb
CHANGED
@@ -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(
|
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(
|
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
|