ruby-picasa 0.2.0

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.
@@ -0,0 +1,221 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper')
2
+
3
+ include RubyPicasa
4
+
5
+ describe 'a RubyPicasa document', :shared => true do
6
+ it 'should have an id' do
7
+ @object.id.should_not be_nil
8
+ end
9
+
10
+ it 'should have an author' do
11
+ unless @no_author
12
+ @object.author.should_not be_nil
13
+ @object.author.name.should == 'Liz'
14
+ @object.author.uri.should == 'http://picasaweb.google.com/liz'
15
+ end
16
+ end
17
+
18
+ it 'should get links by name' do
19
+ @object.link('abc').should be_nil
20
+ @object.link('self').href.should_not be_nil
21
+ end
22
+
23
+ it 'should do nothing for previous and next' do
24
+ @object.previous.should be_nil if @object.link('previous').nil?
25
+ @object.next.should be_nil if @object.link('next').nil?
26
+ end
27
+
28
+ it 'should load' do
29
+ @object.session.expects(:get_url).with(@object.id, {})
30
+ @object.load
31
+ end
32
+
33
+ it 'should have links' do
34
+ @object.links.should_not be_empty
35
+ @object.links.each do |l|
36
+ l.should be_an_instance_of(Objectify::Atom::Link)
37
+ end
38
+ end
39
+
40
+ describe 'session' do
41
+ it 'should return @session' do
42
+ @object.session = :sess
43
+ @object.session.should == :sess
44
+ end
45
+
46
+ it 'should get the parent session' do
47
+ @object.session = nil
48
+ @parent.expects(:session).returns(:parent_sess)
49
+ @object.session.should == :parent_sess
50
+ end
51
+
52
+ it 'should be nil if no parent' do
53
+ @object.session = nil
54
+ @object.expects(:parent).returns nil
55
+ @object.session.should be_nil
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ describe User do
62
+ it_should_behave_like 'a RubyPicasa document'
63
+
64
+ before :all do
65
+ @xml = open_file('user.atom').read
66
+ end
67
+
68
+ before do
69
+ @parent = mock('parent')
70
+ @object = @user = User.new(@xml, @parent)
71
+ @user.session = mock('session')
72
+ end
73
+
74
+ it 'should have albums' do
75
+ @user.albums.length.should == 1
76
+ @user.albums.first.should be_an_instance_of(Album)
77
+ end
78
+ end
79
+
80
+ describe RecentPhotos do
81
+ it_should_behave_like 'a RubyPicasa document'
82
+
83
+ before :all do
84
+ @xml = open_file('recent.atom').read
85
+ end
86
+
87
+ before do
88
+ @parent = mock('parent')
89
+ @object = @album = RecentPhotos.new(@xml, @parent)
90
+ @album.session = mock('session')
91
+ end
92
+
93
+ it 'should have 1 photo' do
94
+ @album.photos.length.should == 1
95
+ @album.photos.first.should be_an_instance_of(Photo)
96
+ end
97
+
98
+ it 'should request next' do
99
+ @album.session.expects(:get_url).with('http://picasaweb.google.com/data/feed/api/user/liz?start-index=2&max-results=1&kind=photo').returns(:result)
100
+ @album.next.should == :result
101
+ end
102
+
103
+ it 'should not request previous on first page' do
104
+ @album.session.expects(:get_url).never
105
+ @album.previous.should be_nil
106
+ end
107
+ end
108
+
109
+ describe Album do
110
+ it_should_behave_like 'a RubyPicasa document'
111
+
112
+ before :all do
113
+ @xml = open_file('album.atom').read
114
+ end
115
+
116
+ before do
117
+ @parent = mock('parent')
118
+ @object = @album = Album.new(@xml, @parent)
119
+ @album.session = mock('session')
120
+ end
121
+
122
+ it 'should have 1 entry' do
123
+ @album.entries.length.should == 1
124
+ end
125
+
126
+ it 'should get links by name' do
127
+ @album.link('abc').should be_nil
128
+ @album.link('alternate').href.should == 'http://picasaweb.google.com/liz/Lolcats'
129
+ end
130
+
131
+ describe 'photos' do
132
+ it 'should use entries if available' do
133
+ @album.expects(:session).never
134
+ @album.photos.should == @album.entries
135
+ end
136
+
137
+ it 'should request photos if needed' do
138
+ @album.entries = []
139
+ new_album = mock('album', :entries => [:photo])
140
+ @album.session.expects(:album).with(@album.id, {}).returns(new_album)
141
+ @album.photos.should == [:photo]
142
+ end
143
+ end
144
+
145
+ it 'should be public' do
146
+ @album.public?.should be_true
147
+ end
148
+
149
+ it 'should not be private' do
150
+ @album.private?.should be_false
151
+ end
152
+
153
+ describe 'first Photo' do
154
+ before do
155
+ @photo = @album.entries.first
156
+ @photo.should be_an_instance_of(Photo)
157
+ end
158
+
159
+ it 'should have a parent' do
160
+ @photo.parent.should == @album
161
+ end
162
+
163
+ it 'should not have an author' do
164
+ @photo.author.should be_nil
165
+ end
166
+
167
+ it 'should have a content' do
168
+ @photo.content.should be_an_instance_of(PhotoUrl)
169
+ end
170
+
171
+ it 'should have 3 thumbnails' do
172
+ @photo.thumbnails.length.should == 3
173
+ @photo.thumbnails.each do |t|
174
+ t.should be_an_instance_of(ThumbnailUrl)
175
+ end
176
+ end
177
+
178
+ it 'should have a default url' do
179
+ @photo.url.should == 'http://lh5.ggpht.com/liz/SKXR5BoXabI/AAAAAAAAAzs/tJQefyM4mFw/invisible_bike.jpg'
180
+ end
181
+
182
+ it 'should have thumbnail urls' do
183
+ @photo.url('s72').should == 'http://lh5.ggpht.com/liz/SKXR5BoXabI/AAAAAAAAAzs/tJQefyM4mFw/s72/invisible_bike.jpg'
184
+ end
185
+
186
+ it 'should have thumbnail info' do
187
+ @photo.thumbnail('s72').width.should == 72
188
+ end
189
+ end
190
+ end
191
+
192
+ describe Search do
193
+ it_should_behave_like 'a RubyPicasa document'
194
+
195
+ before :all do
196
+ @xml = open_file('search.atom').read
197
+ end
198
+
199
+ before do
200
+ @no_author = true
201
+ @parent = mock('parent')
202
+ @object = @search = Search.new(@xml, @parent)
203
+ @search.session = mock('session')
204
+ end
205
+
206
+ it 'should have 1 entry' do
207
+ @search.entries.length.should == 1
208
+ @search.entries.first.should be_an_instance_of(Photo)
209
+ end
210
+
211
+ it 'should request next' do
212
+ @search.session.expects(:get_url).with('http://picasaweb.google.com/data/feed/api/all?q=puppy&start-index=3&max-results=1').returns(:result)
213
+ @search.next.should == :result
214
+ end
215
+
216
+ it 'should request previous' do
217
+ @search.session.expects(:get_url).with('http://picasaweb.google.com/data/feed/api/all?q=puppy&start-index=1&max-results=1').returns(:result)
218
+ @search.previous.should == :result
219
+ end
220
+ end
221
+
@@ -0,0 +1,319 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ class Picasa
4
+ class << self
5
+ public :parse_url
6
+ end
7
+ public :auth_header, :with_cache, :class_from_xml, :xml_data
8
+ end
9
+
10
+ describe 'Picasa class methods' do
11
+ it 'should generate an authorization_url' do
12
+ return_url = 'http://example.com/example?example=ex'
13
+ url = Picasa.authorization_url(return_url)
14
+ url.should include(CGI.escape(return_url))
15
+ url.should match(/session=1/)
16
+ end
17
+
18
+ describe 'token_from_request' do
19
+ it 'should pluck the token from the request' do
20
+ request = mock('request', :params => { 'token' => 'abc' })
21
+ Picasa.token_from_request(request).should == 'abc'
22
+ end
23
+ it 'should raise if no token is present' do
24
+ request = mock('request', :params => { })
25
+ lambda do
26
+ Picasa.token_from_request(request)
27
+ end.should raise_error(RubyPicasa::PicasaTokenError)
28
+ end
29
+ end
30
+
31
+ it 'should authorize a request' do
32
+ Picasa.expects(:token_from_request).with(:request).returns('abc')
33
+ picasa = mock('picasa')
34
+ Picasa.expects(:new).with('abc').returns(picasa)
35
+ picasa.expects(:authorize_token!).with()
36
+ Picasa.authorize_request(:request).should == picasa
37
+ end
38
+
39
+ it 'should recognize absolute urls' do
40
+ Picasa.is_url?('http://something.com').should be_true
41
+ Picasa.is_url?('https://something.com').should be_true
42
+ Picasa.is_url?('12323412341').should_not be_true
43
+ end
44
+
45
+ it 'should recognize relative urls?' do
46
+ pending 'not currently needed'
47
+ Picasa.is_url?('something.com/else').should be_true
48
+ Picasa.is_url?('/else').should be_true
49
+ end
50
+
51
+ describe 'path' do
52
+ it 'should use parse_url and add options' do
53
+ Picasa.expects(:parse_url).with({}).returns(['url', {'a' => 'b'}])
54
+ Picasa.path({}).should ==
55
+ "url?a=b"
56
+ end
57
+ it 'should build the url from user_id and album_id and add options' do
58
+ hash = { :user_id => '123', :album_id => '321' }
59
+ Picasa.expects(:parse_url).with(hash).returns([nil, {}])
60
+ Picasa.path(hash).should ==
61
+ "/data/feed/api/user/123/albumid/321?kind=photo"
62
+ end
63
+ it 'should build the url from special user_id all' do
64
+ hash = { :user_id => 'all' }
65
+ Picasa.expects(:parse_url).with(hash).returns([nil, {}])
66
+ Picasa.path(hash).should ==
67
+ "/data/feed/api/all"
68
+ end
69
+ [ :max_results, :start_index, :tag, :q, :kind,
70
+ :access, :thumbsize, :imgmax, :bbox, :l].each do |arg|
71
+ it "should add #{ arg } to options" do
72
+ Picasa.path(:url => 'url', arg => '!value').should ==
73
+ "url?#{ arg.to_s.dasherize }=%21value"
74
+ end
75
+ end
76
+ it 'should ignore unknown options' do
77
+ Picasa.path(:url => 'place', :eggs => 'over_easy').should == 'place'
78
+ end
79
+ end
80
+
81
+ describe 'parse_url' do
82
+ it 'should prefer url' do
83
+ hash = { :url => 'url', :user_id => 'user_id', :album_id => 'album_id' }
84
+ Picasa.parse_url(hash).should == ['url', {}]
85
+ end
86
+ it 'should next prefer user_id' do
87
+ Picasa.stubs(:is_url?).returns true
88
+ hash = { :user_id => 'user_id', :album_id => 'album_id' }
89
+ Picasa.parse_url(hash).should == ['user_id', {}]
90
+ end
91
+ it 'should use album_id' do
92
+ Picasa.stubs(:is_url?).returns true
93
+ hash = { :album_id => 'album_id' }
94
+ Picasa.parse_url(hash).should == ['album_id', {}]
95
+ end
96
+ it 'should split up the params' do
97
+ hash = { :url => 'url?specs=fun%21' }
98
+ Picasa.parse_url(hash).should == ['url', { 'specs' => 'fun!' }]
99
+ end
100
+ it 'should not use non-url user_id or album_id' do
101
+ hash = { :user_id => 'user_id', :album_id => 'album_id' }
102
+ Picasa.parse_url(hash).should == [nil, {}]
103
+ end
104
+ it 'should handle with no relevant options' do
105
+ hash = { :saoetu => 'aeu' }
106
+ Picasa.parse_url(hash).should == [nil, {}]
107
+ end
108
+ end
109
+ end
110
+
111
+ describe Picasa do
112
+ def body(text)
113
+ #open_file('user_feed.atom').read
114
+ @response.stubs(:body).returns(text)
115
+ end
116
+
117
+ before do
118
+ @response = mock('response')
119
+ @response.stubs(:code).returns '200'
120
+ @http = mock('http')
121
+ @http.stubs(:get).returns @response
122
+ Net::HTTP.stubs(:new).returns(@http)
123
+ @p = Picasa.new 'token'
124
+ end
125
+
126
+ it 'should initialize' do
127
+ @p.token.should == 'token'
128
+ end
129
+
130
+ describe 'authorize_token!' do
131
+ before do
132
+ @p.expects(:auth_header).returns('Authorization' => 'etc')
133
+ @http.expects(:use_ssl=).with true
134
+ @http.expects(:get).with('/accounts/accounts/AuthSubSessionToken',
135
+ 'Authorization' => 'etc').returns(@response)
136
+ end
137
+
138
+ it 'should set the new token' do
139
+ body 'Token=hello'
140
+ @p.authorize_token!
141
+ @p.token.should == 'hello'
142
+ end
143
+
144
+ it 'should raise if the token is not found' do
145
+ body 'nothing to see here'
146
+ lambda do
147
+ @p.authorize_token!
148
+ end.should raise_error(RubyPicasa::PicasaTokenError)
149
+ @p.token.should == 'token'
150
+ end
151
+ end
152
+
153
+ it 'should get the user' do
154
+ @p.expects(:get).with(:user_id => 'default')
155
+ @p.user
156
+ end
157
+
158
+ it 'should get an album' do
159
+ @p.expects(:get).with(:album_id => 'album')
160
+ @p.album('album')
161
+ end
162
+
163
+ it 'should get a url' do
164
+ @p.expects(:get).with(:url => 'the url')
165
+ @p.get_url('the url')
166
+ end
167
+
168
+ describe 'search' do
169
+ it 'should prefer given options' do
170
+ @p.expects(:get).with(:q => 'q', :max_results => 20, :user_id => 'me', :kind => 'comments')
171
+ @p.search('q', :max_results => 20, :user_id => 'me', :kind => 'comments', :q => 'wrong')
172
+ end
173
+ it 'should have good defaults' do
174
+ @p.expects(:get).with(:q => 'q', :max_results => 10, :user_id => 'all', :kind => 'photo')
175
+ @p.search('q')
176
+ end
177
+ end
178
+
179
+ it 'should get recent photos' do
180
+ @p.expects(:get).with(:user_id => 'default', :recent_photos => true, :max_results => 10)
181
+ @p.recent_photos :max_results => 10
182
+ end
183
+
184
+ describe 'album_by_title' do
185
+ before do
186
+ @a1 = mock('a1')
187
+ @a2 = mock('a2')
188
+ @a1.stubs(:title).returns('a1')
189
+ @a2.stubs(:title).returns('a2')
190
+ albums = [ @a1, @a2 ]
191
+ user = mock('user', :albums => albums)
192
+ @p.expects(:user).returns(user)
193
+ end
194
+
195
+ it 'should match the title string' do
196
+ @a2.expects(:load).with({}).returns :result
197
+ @p.album_by_title('a2').should == :result
198
+ end
199
+
200
+ it 'should match a regex' do
201
+ @a1.expects(:load).with({}).returns :result
202
+ @p.album_by_title(/a\d/).should == :result
203
+ end
204
+
205
+ it 'should return nil' do
206
+ @p.album_by_title('zzz').should be_nil
207
+ end
208
+ end
209
+
210
+ describe 'xml' do
211
+ it 'should return the body with a 200 status' do
212
+ body 'xml goes here'
213
+ @p.xml.should == 'xml goes here'
214
+ end
215
+ it 'should return nil with a non-200 status' do
216
+ body 'xml goes here'
217
+ @response.expects(:code).returns '404'
218
+ @p.xml.should be_nil
219
+ end
220
+ end
221
+
222
+ describe 'get' do
223
+ it 'should call class_from_xml if with_cache yields' do
224
+ @p.expects(:with_cache).with({}).yields(:xml).returns(:result)
225
+ @p.expects(:class_from_xml).with(:xml)
226
+ @p.get.should == :result
227
+ end
228
+
229
+ it 'should do nothing if with_cache does not yield' do
230
+ @p.expects(:with_cache).with({}) # doesn't yield
231
+ @p.expects(:class_from_xml).never
232
+ @p.get.should be_nil
233
+ end
234
+ end
235
+
236
+ describe 'auth_header' do
237
+ it 'should build an AuthSub header' do
238
+ @p.auth_header.should == { "Authorization" => %{AuthSub token="token"} }
239
+ end
240
+
241
+ it 'should do nothing' do
242
+ p = Picasa.new nil
243
+ p.auth_header.should == { }
244
+ end
245
+ end
246
+
247
+ describe 'with_cache' do
248
+ it 'yields fresh xml' do
249
+ body 'fresh xml'
250
+ yielded = false
251
+ @p.with_cache(:url => 'place') do |xml|
252
+ yielded = true
253
+ xml.should == 'fresh xml'
254
+ end
255
+ yielded.should be_true
256
+ end
257
+
258
+ it 'yields cached xml' do
259
+ @p.instance_variable_get('@request_cache')['place'] = 'some xml'
260
+ yielded = false
261
+ @p.with_cache(:url => 'place') do |xml|
262
+ yielded = true
263
+ xml.should == 'some xml'
264
+ end
265
+ yielded.should be_true
266
+ end
267
+ end
268
+
269
+ describe 'xml_data' do
270
+ it 'should extract categories from the xml' do
271
+ xml, feed_schema, entry_schema = @p.xml_data(open_file('album.atom'))
272
+ xml.should be_an_instance_of(Nokogiri::XML::Element)
273
+ feed_schema.should == 'http://schemas.google.com/photos/2007#album'
274
+ entry_schema.should == 'http://schemas.google.com/photos/2007#photo'
275
+ end
276
+
277
+ it 'should handle nil' do
278
+ xml, feed_schema, entry_schema = @p.xml_data(nil)
279
+ xml.should be_nil
280
+ end
281
+
282
+ it 'should handle bad xml' do
283
+ xml, feed_schema, entry_schema = @p.xml_data('<entry>something went wrong')
284
+ xml.should_not be_nil
285
+ feed_schema.should be_nil
286
+ entry_schema.should be_nil
287
+ end
288
+ end
289
+
290
+ describe 'class_from_xml' do
291
+ before do
292
+ @user = 'http://schemas.google.com/photos/2007#user'
293
+ @album = 'http://schemas.google.com/photos/2007#album'
294
+ @photo = 'http://schemas.google.com/photos/2007#photo'
295
+ end
296
+
297
+ describe 'valid feed category types' do
298
+ def to_create(klass, feed, entry)
299
+ @object = mock('object', :session= => nil)
300
+ @p.expects(:xml_data).with(:xml).returns([:xml, feed, entry])
301
+ klass.expects(:new).with(:xml, @p).returns(@object)
302
+ @p.class_from_xml(:xml)
303
+ end
304
+ it('user album') { to_create RubyPicasa::User, @user, @album }
305
+ it('user photo') { to_create RubyPicasa::RecentPhotos, @user, @photo }
306
+ it('album nil') { to_create RubyPicasa::Album, @album, nil }
307
+ it('album photo') { to_create RubyPicasa::Album, @album, @photo }
308
+ it('photo nil') { to_create RubyPicasa::Photo, @photo, nil }
309
+ it('photo photo') { to_create RubyPicasa::Search, @photo, @photo }
310
+ end
311
+
312
+ it 'should raise an error for invalid feed category types' do
313
+ @p.expects(:xml_data).with(:xml).returns([:xml, @album, @user])
314
+ lambda do
315
+ @p.class_from_xml(:xml)
316
+ end.should raise_error(RubyPicasa::PicasaError)
317
+ end
318
+ end
319
+ end