ruby-picasa 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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