bitsa 0.20 → 0.30

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ require "tempfile"
2
+ require "helper"
3
+ require "bitsa/config_file"
4
+
5
+ describe Bitsa::ConfigFile do
6
+ context "An existing configuration file should load from disk" do
7
+ let(:config) { Bitsa::ConfigFile.new("spec/data/config.yml")}
8
+ context :login do
9
+ specify { expect(config.data[:login]).to eq("test@gmail.com") }
10
+ end
11
+ context :password do
12
+ specify { expect(config.data[:password]).to eq("myPassword") }
13
+ end
14
+ end
15
+
16
+ context "data from a non-existent configuration files" do
17
+ let(:config) { Bitsa::ConfigFile.new("/tmp/i-dont-exist") }
18
+ specify { expect(config.data).to eq({}) }
19
+ end
20
+
21
+ context "that I have no rights to" do
22
+ let(:tmp_file) {
23
+ tmp_file = Tempfile.open("cache")
24
+ FileUtils.cp("spec/data/config.yml", tmp_file.path)
25
+ FileUtils.chmod(0000, tmp_file.path)
26
+ tmp_file
27
+ }
28
+
29
+ context "reading" do
30
+ specify { expect { Bitsa::ConfigFile.new(tmp_file.path) }.to raise_error(Errno::EACCES) }
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,331 @@
1
+ require "fileutils"
2
+ require "tempfile"
3
+ require "yaml"
4
+
5
+ require "helper"
6
+
7
+ require "bitsa/contacts_cache"
8
+
9
+ def create_empty_temp_file
10
+ tmp_file = Tempfile.open("cache")
11
+ File.open(tmp_file.path, "w") do |f|
12
+ f.write('')
13
+ end
14
+ tmp_file
15
+ end
16
+
17
+ def read_test_data
18
+ # 4 users, 5 email addresses
19
+ YAML::load_file("spec/data/bitsa_cache.yml")[1].values.flatten(1).sort
20
+ end
21
+
22
+ def create_cache(last_modified = nil, lifespan_days = 1)
23
+ source_last_modified, addresses = YAML::load_file("spec/data/bitsa_cache.yml")
24
+ source_last_modified = last_modified.to_s if last_modified
25
+
26
+ tmp_file = Tempfile.open("cache")
27
+ File.open(tmp_file.path, "w") do |f|
28
+ f.write(YAML::dump([source_last_modified, addresses]))
29
+ end
30
+ [Bitsa::ContactsCache.new(tmp_file.path, lifespan_days), tmp_file]
31
+ end
32
+
33
+ RSpec.shared_examples_for "an empty cache" do |c|
34
+ specify { expect(c).not_to be_nil}
35
+ context :size do
36
+ specify { expect(c.size).to eq(0) }
37
+ end
38
+ context "last modified date" do
39
+ specify { expect(c.source_last_modified).to be_nil }
40
+ end
41
+ context :stale do
42
+ specify { expect(c.stale?).to be_truthy }
43
+ end
44
+ end
45
+
46
+ RSpec.shared_examples "a full resultset" do |results|
47
+ specify "should return all entries" do
48
+ expect(results.size).to eq(5)
49
+ expect(results.flatten(0).sort).to eq(read_test_data)
50
+ end
51
+ end
52
+
53
+ describe Bitsa::ContactsCache do
54
+ context "empty cache file" do
55
+ cache = Bitsa::ContactsCache.new(create_empty_temp_file.path, 1)
56
+ it_behaves_like "an empty cache", cache
57
+ end
58
+
59
+ context "cache file does not exist" do
60
+ cache = Bitsa::ContactsCache.new("/tmp/idonotexististhatoky", 1)
61
+ it_behaves_like "an empty cache", cache
62
+ end
63
+
64
+ context "user not authorised to read cache file" do
65
+ let(:tmp_file) do
66
+ tmp_file = create_empty_temp_file
67
+ FileUtils.chmod(0222, tmp_file.path)
68
+ tmp_file
69
+ end
70
+
71
+ specify {
72
+ expect {Bitsa::ContactsCache.new(tmp_file.path, 1)}.to raise_error(Errno::EACCES)
73
+ }
74
+ end
75
+
76
+ context "last updated today and lifespan is 1 day" do
77
+ context :stale do
78
+ specify {expect(create_cache(DateTime.now, 1)[0].stale?).to be_falsey}
79
+ end
80
+ end
81
+
82
+ context "last updated yesterday and lifespan is 1 day" do
83
+ context :stale do
84
+ specify {expect(create_cache(DateTime.now-1, 1)[0].stale?).to be_truthy}
85
+ end
86
+ end
87
+
88
+ context "last updated yesterday and lifespan is 2 days" do
89
+ context :stale do
90
+ specify {expect(create_cache(DateTime.now-1, 2)[0].stale?).to be_falsey}
91
+ end
92
+ end
93
+
94
+ context "last updated 3 days ago and lifespan is 0 days" do
95
+ context :stale do
96
+ specify {expect(create_cache(DateTime.now-3, 0)[0].stale?).to be_falsey}
97
+ end
98
+ end
99
+
100
+ context "last updated 3 days ago and lifespan is -1 days" do
101
+ context :stale do
102
+ specify {expect(create_cache(DateTime.now-3, -1)[0].stale?).to be_falsey}
103
+ end
104
+ end
105
+
106
+ context "after clearing the cache" do
107
+ before(:each) do
108
+ expect(cache.size).to be >= 1
109
+ expect(cache.empty?).to be false
110
+ cache.clear!
111
+ end
112
+ let(:cache) { create_cache[0] }
113
+
114
+ context :size do
115
+ specify {expect(cache.size).to eq(0)}
116
+ end
117
+
118
+ context "empty?" do
119
+ specify {expect(cache.empty?).to be true}
120
+ end
121
+ end
122
+
123
+ context "searching the test data" do
124
+
125
+ specify "should have read the correct number of contacts" do
126
+ expect(create_cache[0].size).to eq(4)
127
+ end
128
+
129
+ context "with an empty string" do
130
+ it_behaves_like "a full resultset", create_cache[0].search("")
131
+ end
132
+
133
+ context "with a nil string" do
134
+ it_behaves_like "a full resultset", create_cache[0].search(nil)
135
+ end
136
+
137
+ context "searching by start of email address" do
138
+ context "returning a single result" do
139
+ let(:expected) { [["test1@example.com", "My Tester"]] }
140
+
141
+ specify {expect(create_cache[0].search('test1')).to match_array expected}
142
+ end
143
+
144
+ context "returning multiple results" do
145
+ let(:expected) {
146
+ [["john_smith@here.org", ""],
147
+ ["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]]
148
+ }
149
+
150
+ specify {expect(create_cache[0].search("jo")).to match_array expected}
151
+ end
152
+ end
153
+
154
+ context "searching by end of email address" do
155
+ let (:expected) {[["john_smith@here.org", ""]]}
156
+
157
+ specify {expect(create_cache[0].search('org')).to match_array expected}
158
+ end
159
+
160
+ context "searching by middle of email address" do
161
+ let (:expected) {[["john_smith@here.org", ""]]}
162
+
163
+ specify {expect(create_cache[0].search('n_s')).to match_array expected}
164
+ end
165
+
166
+ context "searching email using a different case to the actual data" do
167
+ let (:expected) {[["john_smith@here.org", ""]]}
168
+
169
+ specify {expect(create_cache[0].search('N_sMI')).to match_array expected}
170
+ end
171
+
172
+ context "searching by start of name" do
173
+ let(:expected) { [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]] }
174
+
175
+ specify {expect(create_cache[0].search('Joan Blo')).to match_array expected}
176
+ end
177
+
178
+ context "searching by end of name" do
179
+ let(:expected) { [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]] }
180
+
181
+ specify {expect(create_cache[0].search('ggshere')).to match_array expected}
182
+ end
183
+
184
+ context "searching by middle of name" do
185
+ let(:expected) { [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]] }
186
+
187
+ specify {expect(create_cache[0].search('n Bl')).to match_array expected}
188
+ end
189
+
190
+ context "searching by middle of name" do
191
+ let(:expected) { [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]] }
192
+
193
+ specify {expect(create_cache[0].search('n Bl')).to match_array expected}
194
+ end
195
+
196
+ context "searching name using a different case to the actual data" do
197
+ let(:expected) { [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]] }
198
+
199
+ specify {expect(create_cache[0].search('N BL')).to match_array expected}
200
+ end
201
+
202
+ context "search with no results" do
203
+ context :size do
204
+ specify {expect(create_cache[0].search('nothing is here').size).to eq(0)}
205
+ end
206
+ end
207
+
208
+ context "search on one contact with two email addresses" do
209
+ let(:expected) {
210
+ [["email2@somewhere.com", "Mr Multiple"], ["email1@somewhere.com", "Mr Multiple"]]
211
+ }
212
+
213
+ specify {expect(create_cache[0].search('multip')).to match_array expected}
214
+ end
215
+
216
+ context "searching a contact with multiple email addresses where only 1 email address matches" do
217
+ let(:expected) {[["email1@somewhere.com", "Mr Multiple"]]}
218
+
219
+ specify {expect(create_cache[0].search('email1')).to match_array expected}
220
+ end
221
+
222
+ context "find by ID" do
223
+ let(:id) {"http://www.google.com/m8/feeds/contacts/person%40example.org/base/685e301a549c176e"}
224
+ let(:expected) {[["email1@somewhere.com", "Mr Multiple"],
225
+ ["email2@somewhere.com", "Mr Multiple"]]}
226
+
227
+ specify {expect(create_cache[0].get(id)).to match_array expected}
228
+ end
229
+
230
+ context "find by a non-existent ID" do
231
+ let(:id) {"http://www.google.com/m8/feeds/contacts/person%40example.org/base/4783783783"}
232
+
233
+ specify {expect(create_cache[0].get(id)).to be_nil}
234
+ end
235
+ end
236
+
237
+ context "updating the test data" do
238
+ let(:cache) { create_cache[0] }
239
+ let(:id) {"http://www.google.com/m8/feeds/contacts/person%40example.org/base/637e301a549c176e"}
240
+
241
+ specify "should change the data" do
242
+ expect(cache.get(id)).to match_array [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]]
243
+ cache.update(id, "Tammy Smith", ["tammy5@example.com"])
244
+ expect(cache.get(id)).to match_array [["tammy5@example.com", "Tammy Smith"]]
245
+ end
246
+ end
247
+
248
+ context "updating with two email addresses" do
249
+ let(:cache) { create_cache[0] }
250
+ let(:id) {"http://www.google.com/m8/feeds/contacts/person%40example.org/base/637e301a549c176e"}
251
+
252
+ specify "should update the data" do
253
+ expected = [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]]
254
+ expect(cache.get(id)).to match_array expected
255
+
256
+ cache.update(id, "Tammy Smith", ["tammy5@example.com", "smithtammy@exampel.org"])
257
+
258
+ expected = [["smithtammy@exampel.org", "Tammy Smith"], ["tammy5@example.com", "Tammy Smith"]]
259
+ expect(cache.get(id)).to match_array expected
260
+ end
261
+ end
262
+
263
+ context "deleting a non-existent entry" do
264
+ let(:cache) { create_cache[0] }
265
+
266
+ specify "should return nil" do
267
+ expect(cache.delete("NONEXISTENT")).to be_nil
268
+ end
269
+
270
+ specify do
271
+ expect {
272
+ cache.delete("NONEXISTENT")
273
+ }.to_not change(cache, :size)
274
+ end
275
+
276
+ end
277
+
278
+ context "deleting an existing entry" do
279
+ let(:cache) { create_cache[0] }
280
+ let(:id) { "http://www.google.com/m8/feeds/contacts/person%40example.org/base/637e301a549c176e" }
281
+
282
+ specify {expect { cache.delete(id) }.to change(cache, :size).by(-1)}
283
+
284
+ specify "should return the deleted entry" do
285
+ expect(cache.delete(id)).to match_array [["Joan.bloggs@somewhere.com.au", "Joan Bloggshere"]]
286
+ end
287
+
288
+ context "the cache" do
289
+ specify "should no longer contain the deleted entry" do
290
+ cache.delete(id)
291
+ expect(cache.get(id)).to be_nil
292
+ end
293
+ end
294
+ end
295
+
296
+ context "saving" do
297
+ let(:data) { create_cache }
298
+ let(:cache) { data[0] }
299
+ let(:cache_file) { data[1] }
300
+
301
+ context "changes to existing contacts" do
302
+ specify {
303
+ id = "http://www.google.com/m8/feeds/contacts/person%40example.org/base/637e301a549c176e"
304
+ cache.update(id, "The Changed Name", ["change1@somewhere.org"])
305
+ cache.save
306
+ new_cache = Bitsa::ContactsCache.new(cache_file.path, 1)
307
+ expected = [["change1@somewhere.org", "The Changed Name"]]
308
+ expect(new_cache.get(id)).to match_array expected
309
+ }
310
+ end
311
+
312
+ context "adding new entries" do
313
+ specify {
314
+ id = "NONEXISTENT"
315
+ cache.update(id, "The Changed Name", ["change1@somewhere.org"])
316
+ cache.save
317
+ new_cache = Bitsa::ContactsCache.new(cache_file.path, 1)
318
+ expected = [["change1@somewhere.org", "The Changed Name"]]
319
+ expect(new_cache.get(id)).to match_array expected
320
+ }
321
+ end
322
+
323
+ context "to a file that I have no write rights to" do
324
+ before(:each) { FileUtils.chmod(0444, cache_file.path) }
325
+
326
+ specify { expect { cache.save }.to raise_error(Errno::EACCES) }
327
+
328
+ end
329
+ end
330
+
331
+ end
@@ -0,0 +1,203 @@
1
+ require 'helper'
2
+
3
+ require 'fakeweb'
4
+
5
+ require "bitsa/contacts_cache"
6
+ require "bitsa/gmail_contacts_loader"
7
+
8
+ describe Bitsa::GmailContactsLoader do
9
+ context "Bitsa::GmailContactsLoader" do
10
+ before(:each) do
11
+ FakeWeb.allow_net_connect = false
12
+
13
+ # Start session.
14
+ FakeWeb.register_uri(:post, "https://www.google.com/accounts/ClientLogin",
15
+ :body => "SID=DQAAAGgA...7Zg8CTN\nLSID=DQAAAGsA...lk8BBbG\nAuth=DQAAAGgA...dk3fA5N")
16
+
17
+ # First chunk of contacts
18
+ FakeWeb.register_uri(:get, "https://www.google.com/m8/feeds/contacts/test/thin?orderby=lastmodified&showdeleted=true&max-results=15&start-index=1",
19
+ :body => <<eos
20
+ <feed gd:etag='W/&quot;AkANQXo7eCp7ImA9WxFTGUo.&quot;' xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns='http://www.w3.org/2005/Atom'>
21
+ <id>
22
+ somebody@example.com
23
+ </id>
24
+ <updated>
25
+ 2010-04-11T09:39:50.400Z
26
+ </updated>
27
+ <category term='http://schemas.google.com/contact/2008#contact' scheme='http://schemas.google.com/g/2005#kind'/>
28
+ <title>
29
+ Somebodys's Contacts
30
+ </title>
31
+ <link href='http://www.google.com/' rel='alternate' type='text/html'/>
32
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'/>
33
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'/>
34
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/batch' rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml'/>
35
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin?max-results=15' rel='self' type='application/atom+xml'/>
36
+ <author>
37
+ <name>
38
+ Somebody
39
+ </name>
40
+ <email>
41
+ somebody@example.com
42
+ </email>
43
+ </author>
44
+ <generator uri='http://www.google.com/m8/feeds' version='1.0'>
45
+ Contacts
46
+ </generator>
47
+ <openSearch:totalResults>
48
+ 1
49
+ </openSearch:totalResults>
50
+ <openSearch:startIndex>
51
+ 1
52
+ </openSearch:startIndex>
53
+ <openSearch:itemsPerPage>
54
+ 15
55
+ </openSearch:itemsPerPage>
56
+ <entry gd:etag='&quot;SHc-fTVSLyp7ImA9WxBaEkwIQAU.&quot;'>
57
+ <id>
58
+ http://www.google.com/m8/feeds/contacts/somebody%40example.com/base/0
59
+ </id>
60
+ <updated>
61
+ 2010-03-21T23:05:19.955Z
62
+ </updated>
63
+ <app:edited xmlns:app='http://www.w3.org/2007/app'>
64
+ 2010-03-21T23:05:19.955Z
65
+ </app:edited>
66
+ <category term='http://schemas.google.com/contact/2008#contact' scheme='http://schemas.google.com/g/2005#kind'/>
67
+ <title>
68
+ Jpe Bloggs
69
+ </title>
70
+ <link href='http://www.google.com/m8/feeds/photos/media/somebody%40example.com/0' gd:etag='&quot;bwR-YldFbCp7ImBIGHMMbBALQAwWIFMBVVc.&quot;' rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*'/>
71
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/0' rel='self' type='application/atom+xml'/>
72
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/0' rel='edit' type='application/atom+xml'/>
73
+ <gd:email address='jbloggs@example.com' rel='http://schemas.google.com/g/2005#other' primary='true'/>
74
+ <gd:email address='another@example.com' rel='http://schemas.google.com/g/2005#work'/>
75
+ <gContact:groupMembershipInfo href='http://www.google.com/m8/feeds/groups/somebody%40example.com/base/6' deleted='false'/>
76
+ </entry>
77
+ </feed>
78
+ eos
79
+ )
80
+
81
+ # Second chunk of contacts
82
+ FakeWeb.register_uri(:get, "https://www.google.com/m8/feeds/contacts/test/thin?orderby=lastmodified&showdeleted=true&max-results=15&start-index=16",
83
+ :body => <<eos
84
+ <feed gd:etag='W/&quot;AkANQXo7eCp7ImA9WxFTGUo.&quot;' xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns='http://www.w3.org/2005/Atom'>
85
+ <id>
86
+ somebody@example.com
87
+ </id>
88
+ <updated>
89
+ 2010-04-11T09:39:50.400Z
90
+ </updated>
91
+ <category term='http://schemas.google.com/contact/2008#contact' scheme='http://schemas.google.com/g/2005#kind'/>
92
+ <title>
93
+ Somebodys's Contacts
94
+ </title>
95
+ <link href='http://www.google.com/' rel='alternate' type='text/html'/>
96
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'/>
97
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'/>
98
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/batch' rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml'/>
99
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin?max-results=15' rel='self' type='application/atom+xml'/>
100
+ <author>
101
+ <name>
102
+ Somebody
103
+ </name>
104
+ <email>
105
+ somebody@example.com
106
+ </email>
107
+ </author>
108
+ <generator uri='http://www.google.com/m8/feeds' version='1.0'>
109
+ Contacts
110
+ </generator>
111
+ <openSearch:totalResults>
112
+ 1
113
+ </openSearch:totalResults>
114
+ <openSearch:startIndex>
115
+ 1
116
+ </openSearch:startIndex>
117
+ <openSearch:itemsPerPage>
118
+ 15
119
+ </openSearch:itemsPerPage>
120
+ <entry gd:etag='&quot;SHc-fTVSLyp7ImA9WxBaEkwIQAU.&quot;'>
121
+ <id>
122
+ http://www.google.com/m8/feeds/contacts/somebody%40example.com/base/0
123
+ </id>
124
+ <gd:deleted/>
125
+ <updated>
126
+ 2010-03-21T23:05:19.955Z
127
+ </updated>
128
+ <app:edited xmlns:app='http://www.w3.org/2007/app'>
129
+ 2010-03-21T23:05:19.955Z
130
+ </app:edited>
131
+ <category term='http://schemas.google.com/contact/2008#contact' scheme='http://schemas.google.com/g/2005#kind'/>
132
+ <title>
133
+ Jpe Bloggs
134
+ </title>
135
+ <link href='http://www.google.com/m8/feeds/photos/media/somebody%40example.com/0' gd:etag='&quot;bwR-YldFbCp7ImBIGHMMbBALQAwWIFMBVVc.&quot;' rel='http://schemas.google.com/contacts/2008/rel#photo' type='image/*'/>
136
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/0' rel='self' type='application/atom+xml'/>
137
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/0' rel='edit' type='application/atom+xml'/>
138
+ <gd:email address='jbloggs@example.com' rel='http://schemas.google.com/g/2005#other' primary='true'/>
139
+ <gd:email address='another@example.com' rel='http://schemas.google.com/g/2005#work'/>
140
+ <gContact:groupMembershipInfo href='http://www.google.com/m8/feeds/groups/somebody%40example.com/base/6' deleted='false'/>
141
+ </entry>
142
+ </feed>
143
+ eos
144
+ )
145
+
146
+ # Third chunk - empty
147
+ FakeWeb.register_uri(:get, "https://www.google.com/m8/feeds/contacts/test/thin?orderby=lastmodified&showdeleted=true&max-results=15&start-index=31",
148
+ :body => <<eos
149
+ <feed gd:etag='W/&quot;AkANQXo7eCp7ImA9WxFTGUo.&quot;' xmlns:gContact='http://schemas.google.com/contact/2008' xmlns:gd='http://schemas.google.com/g/2005' xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:openSearch='http://a9.com/-/spec/opensearch/1.1/' xmlns='http://www.w3.org/2005/Atom'>
150
+ <id>
151
+ somebody@example.com
152
+ </id>
153
+ <updated>
154
+ 2010-04-11T09:39:50.400Z
155
+ </updated>
156
+ <category term='http://schemas.google.com/contact/2008#contact' scheme='http://schemas.google.com/g/2005#kind'/>
157
+ <title>
158
+ Somebodys's Contacts
159
+ </title>
160
+ <link href='http://www.google.com/' rel='alternate' type='text/html'/>
161
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml'/>
162
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin' rel='http://schemas.google.com/g/2005#post' type='application/atom+xml'/>
163
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin/batch' rel='http://schemas.google.com/g/2005#batch' type='application/atom+xml'/>
164
+ <link href='http://www.google.com/m8/feeds/contacts/somebody%40example.com/thin?max-results=15' rel='self' type='application/atom+xml'/>
165
+ <author>
166
+ <name>
167
+ Somebody
168
+ </name>
169
+ <email>
170
+ somebody@example.com
171
+ </email>
172
+ </author>
173
+ <generator uri='http://www.google.com/m8/feeds' version='1.0'>
174
+ Contacts
175
+ </generator>
176
+ <openSearch:totalResults>
177
+ 0
178
+ </openSearch:totalResults>
179
+ <openSearch:startIndex>
180
+ 0
181
+ </openSearch:startIndex>
182
+ <openSearch:itemsPerPage>
183
+ 15
184
+ </openSearch:itemsPerPage>
185
+ </feed>
186
+ eos
187
+ )
188
+
189
+ end
190
+
191
+ it "should update cache" do
192
+ cache = double('Bitsa::ContactsCache')
193
+ gcl = Bitsa::GmailContactsLoader.new('test', 'pw', 15)
194
+ expect(cache).to receive(:update).once
195
+ expect(cache).to receive(:source_last_modified=).once
196
+ expect(cache).to receive(:source_last_modified).exactly(3)
197
+ expect(cache).to receive(:save).once
198
+ expect(cache).to receive(:delete).once
199
+ gcl.update_cache(cache)
200
+ end
201
+
202
+ end
203
+ end