atom-tools 0.9.0 → 2.0.5

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.
Files changed (93) hide show
  1. data/COPYING +3 -3
  2. data/README +9 -49
  3. data/Rakefile +65 -62
  4. data/bin/atom-cp +159 -0
  5. data/bin/atom-grep +78 -0
  6. data/bin/atom-post +73 -0
  7. data/bin/atom-purge +82 -0
  8. data/doc/classes/Atom/AttrEl.html +163 -0
  9. data/doc/classes/Atom/Author.html +2 -19
  10. data/doc/classes/Atom/AutodiscoveryFailure.html +111 -0
  11. data/doc/classes/Atom/Categories.html +168 -0
  12. data/doc/classes/Atom/Category.html +10 -1
  13. data/doc/classes/Atom/Collection.html +194 -91
  14. data/doc/classes/Atom/Content.html +42 -1
  15. data/doc/classes/Atom/Contributor.html +2 -8
  16. data/doc/classes/Atom/Control.html +126 -0
  17. data/doc/classes/Atom/Converters.html +571 -0
  18. data/doc/classes/Atom/DigestAuth.html +285 -0
  19. data/doc/classes/Atom/Element.html +673 -106
  20. data/doc/classes/Atom/FileCache.html +287 -0
  21. data/doc/classes/Atom/HTTP.html +211 -95
  22. data/doc/classes/Atom/HTTPResponse.html +190 -0
  23. data/doc/classes/Atom/HasCategories.html +187 -0
  24. data/doc/classes/Atom/HasLinks.html +168 -0
  25. data/doc/classes/Atom/Link.html +80 -3
  26. data/doc/classes/Atom/NilCache.html +202 -0
  27. data/doc/classes/Atom/ParseError.html +111 -0
  28. data/doc/classes/Atom/Parsers.html +311 -0
  29. data/doc/classes/Atom/Person.html +130 -0
  30. data/doc/classes/Atom/Rights.html +113 -0
  31. data/doc/classes/Atom/Service.html +298 -0
  32. data/doc/{files/lib/atom/yaml_rb.html → classes/Atom/Source.html} +26 -24
  33. data/doc/classes/Atom/Subtitle.html +113 -0
  34. data/doc/classes/Atom/Summary.html +113 -0
  35. data/doc/classes/Atom/Text.html +196 -53
  36. data/doc/classes/Atom/Title.html +113 -0
  37. data/doc/classes/Atom/Tools.html +525 -0
  38. data/doc/classes/Atom/Workspace.html +121 -0
  39. data/doc/classes/Object.html +204 -0
  40. data/doc/created.rid +1 -1
  41. data/doc/files/README.html +9 -49
  42. data/doc/files/lib/atom/cache_rb.html +302 -0
  43. data/doc/files/lib/atom/collection_rb.html +1 -2
  44. data/doc/files/lib/atom/element_rb.html +2 -1
  45. data/doc/files/lib/atom/entry_rb.html +2 -2
  46. data/doc/files/lib/atom/feed_rb.html +1 -2
  47. data/doc/files/lib/atom/http_rb.html +6 -1
  48. data/doc/files/lib/atom/{xml_rb.html → service_rb.html} +8 -6
  49. data/doc/files/lib/atom/text_rb.html +2 -1
  50. data/doc/files/lib/atom/{app_rb.html → tools_rb.html} +4 -6
  51. data/doc/fr_class_index.html +23 -3
  52. data/doc/fr_file_index.html +3 -3
  53. data/doc/fr_method_index.html +111 -36
  54. data/lib/atom/cache.rb +178 -0
  55. data/lib/atom/collection.rb +90 -40
  56. data/lib/atom/element.rb +569 -161
  57. data/lib/atom/entry.rb +63 -89
  58. data/lib/atom/feed.rb +65 -67
  59. data/lib/atom/http.rb +421 -49
  60. data/lib/atom/service.rb +106 -0
  61. data/lib/atom/text.rb +138 -61
  62. data/lib/atom/tools.rb +163 -0
  63. data/spec/entry_spec.rb +364 -0
  64. data/spec/ext_spec.rb +42 -0
  65. data/spec/feed_spec.rb +39 -0
  66. data/spec/fixtures/contacts-feed.xml +34 -0
  67. data/spec/fixtures/entry-w-ext.xml +15 -0
  68. data/spec/fixtures/entry-w-xml.xml +24 -0
  69. data/spec/fixtures/entry.xml +42 -0
  70. data/spec/fixtures/feed-w-ext.xml +33 -0
  71. data/spec/fixtures/service-w-xhtml-ns.xml +21 -0
  72. data/spec/fixtures/service.xml +36 -0
  73. data/spec/service_spec.rb +108 -0
  74. data/spec/spec_helper.rb +9 -0
  75. data/test/conformance/order.rb +11 -10
  76. data/test/conformance/title.rb +9 -9
  77. data/test/conformance/updated.rb +2 -1
  78. data/test/runtests.rb +0 -0
  79. data/test/test_constructs.rb +78 -8
  80. data/test/test_feed.rb +35 -29
  81. data/test/test_general.rb +3 -30
  82. data/test/test_http.rb +248 -34
  83. data/test/test_protocol.rb +143 -24
  84. data/test/test_xml.rb +145 -53
  85. metadata +116 -62
  86. data/bin/atom-client.rb +0 -246
  87. data/bin/atom-server.rb~ +0 -71
  88. data/doc/classes/Atom/App.html +0 -217
  89. data/doc/classes/Atom/Entry.html +0 -365
  90. data/doc/classes/Atom/Feed.html +0 -585
  91. data/lib/atom/app.rb +0 -87
  92. data/lib/atom/xml.rb +0 -200
  93. data/lib/atom/yaml.rb +0 -101
data/test/test_http.rb CHANGED
@@ -1,7 +1,18 @@
1
+ require "test/unit"
2
+
1
3
  require "atom/http"
2
4
  require "webrick"
3
5
 
4
- class AtomProtocolTest < Test::Unit::TestCase
6
+ class AtomHTTPTest < Test::Unit::TestCase
7
+ REALM = "test authentication"
8
+ USER = "test_user"
9
+ PASS = "aoeuaoeu"
10
+
11
+ # for Google AuthSub authentication
12
+ TOKEN = "pq7266382__838"
13
+
14
+ SECRET_DATA = "I kissed a boy once"
15
+
5
16
  def setup
6
17
  @http = Atom::HTTP.new
7
18
  @port = rand(1024) + 1024
@@ -11,76 +22,279 @@ class AtomProtocolTest < Test::Unit::TestCase
11
22
  end
12
23
 
13
24
  def test_parse_wwwauth
14
- header = 'Basic realm="SokEvo"'
15
-
16
- # parse_wwwauth is a private method
17
- auth_type, auth_params = @http.send :parse_wwwauth, header
25
+ # a Basic WWW-Authenticate
26
+ header = 'realm="SokEvo"'
27
+
28
+ params = @http.send :parse_quoted_wwwauth, header
29
+ assert_equal "SokEvo", params[:realm]
30
+
31
+ # Digest is parsed a bit differently
32
+ header = 'opaque="07UrfUiCYac5BbWJ", algorithm=MD5-sess, qop="auth", stale=TRUE, nonce="MDAx0Mzk", realm="test authentication"'
33
+
34
+ params = @http.send :parse_wwwauth_digest, header
18
35
 
19
- assert_equal "Basic", auth_type
20
- assert_equal "SokEvo", auth_params["realm"]
36
+ assert_equal "test authentication", params[:realm]
37
+ assert_equal "MDAx0Mzk", params[:nonce]
38
+ assert_equal true, params[:stale]
39
+ assert_equal "auth", params[:qop]
40
+ assert_equal "MD5-sess", params[:algorithm]
41
+ assert_equal "07UrfUiCYac5BbWJ", params[:opaque]
21
42
  end
22
43
 
23
44
  def test_GET
24
- @s.mount_proc("/") do |req,res|
45
+ mount_one_shot do |req,res|
25
46
  assert_equal("/", req.path)
26
47
 
27
48
  res.content_type = "text/plain"
28
- res.body = "just junk"
29
-
30
- @s.stop
49
+ res.body = "Success!"
31
50
  end
32
51
 
33
- one_shot
34
-
35
- res = get_root
52
+ get_root
36
53
 
37
- assert_equal("200", res.code)
38
- assert_equal("text/plain", res.content_type)
39
- assert_equal("just junk", res.body)
54
+ assert_equal "200", @res.code
55
+ assert_equal "text/plain", @res.content_type
56
+ assert_equal "Success!", @res.body
40
57
  end
41
58
 
42
59
  def test_GET_headers
43
- @s.mount_proc("/") do |req,res|
60
+ mount_one_shot do |req,res|
44
61
  assert_equal("tester agent", req["User-Agent"])
62
+ end
63
+
64
+ get_root("User-Agent" => "tester agent")
65
+
66
+ assert_equal "200", @res.code
67
+ end
68
+
69
+ def test_redirect
70
+ @s.mount_proc("/") do |req,res|
71
+ res.status = 302
72
+ res["Location"] = "http://localhost:#{@port}/redirected"
45
73
 
74
+ res.body = "ignore me."
75
+ end
76
+
77
+ @s.mount_proc("/redirected") do |req,res|
78
+ res.content_type = "text/plain"
79
+ res.body = "Success!"
80
+
46
81
  @s.stop
47
82
  end
48
83
 
84
+ one_shot; get_root
85
+
86
+ # the redirect should be transparent (to whatever extent it can be)
87
+ assert_equal "200", @res.code
88
+ assert_equal "Success!", @res.body
89
+ end
90
+
91
+ def test_redirect_loop
92
+ @s.mount_proc("/") do |req,res|
93
+ res.status = 302
94
+ res["Location"] = "http://localhost:#{@port}/redirected"
95
+ end
96
+
97
+ @s.mount_proc("/redirected") do |req,res|
98
+ res.status = 302
99
+ res["Location"] = "http://localhost:#{@port}/"
100
+ end
101
+
102
+ one_shot
103
+
104
+ assert_raises(Atom::HTTPException) { get_root }
105
+
106
+ @s.stop
107
+ end
108
+
109
+ def test_redirect_non_GET_non_HEAD
110
+ @s.mount_proc("/") do |req,res|
111
+ assert_equal "POST", req.request_method
112
+ res.status = 302
113
+ res["Location"] = "http://localhost:#{@port}/redirected"
114
+ end
115
+
116
+ @s.mount_proc("/redirected") do |req,res|
117
+ assert_equal "POST", req.request_method
118
+ assert_equal "important message", req.body
119
+ res.content_type = "text/plain"
120
+ res.body = "Success!"
121
+ end
122
+
49
123
  one_shot
50
124
 
51
- res = get_root("User-Agent" => "tester agent")
125
+ @res = @http.post "http://localhost:#{@port}/", "important message"
52
126
 
53
- assert_equal("200", res.code)
127
+ assert_equal "302", @res.code
128
+
129
+ @http.allow_all_redirects = true
130
+
131
+ one_shot
132
+
133
+ @res = @http.post "http://localhost:#{@port}/", "important message"
134
+
135
+ assert_equal "200", @res.code
136
+ assert_equal "Success!", @res.body
137
+
138
+ @s.stop
54
139
  end
55
140
 
56
141
  def test_basic_auth
57
- @s.mount_proc("/") do |req,res|
58
- WEBrick::HTTPAuth.basic_auth(req, res, "test authentication") do |u,p|
59
- u == "test" and p == "pass"
142
+ mount_one_shot do |req,res|
143
+ WEBrick::HTTPAuth.basic_auth(req, res, REALM) do |u,p|
144
+ u == USER and p == PASS
60
145
  end
61
146
 
62
- res.body = "sooper-secret!"
63
- @s.stop
147
+ res.body = SECRET_DATA
64
148
  end
65
149
 
150
+ # no credentials
151
+ assert_raises(Atom::Unauthorized) { get_root }
152
+
153
+ # incorrect credentials
154
+ @http.user = USER
155
+ @http.pass = "incorrect_password"
156
+
66
157
  one_shot
67
-
158
+
159
+ assert_raises(Atom::Unauthorized) { get_root }
160
+
161
+ # no credentials, fancy block
162
+ @http.when_auth do nil end
163
+
164
+ one_shot
165
+
68
166
  assert_raises(Atom::Unauthorized) { get_root }
69
167
 
168
+ # correct credentials, fancy block
70
169
  @http.when_auth do |abs_url,realm|
71
170
  assert_equal "http://localhost:#{@port}/", abs_url
72
- assert_equal "test authentication", realm
171
+ assert_equal REALM, realm
73
172
 
74
- ["test", "pass"]
173
+ [USER, PASS]
75
174
  end
76
-
175
+
77
176
  one_shot
78
-
79
- res = get_root
80
- assert_equal("200", res.code)
81
- assert_equal("sooper-secret!", res.body)
177
+
178
+ assert_authenticates
179
+ end
180
+
181
+ def test_digest_auth
182
+ # a dummy userdb (saves me creating a file)
183
+ userdb = {}
184
+ # with a single entry
185
+ userdb[USER] = PASS
186
+
187
+ # HTTPAuth::DigestAuth#authenticate uses this
188
+ def userdb.get_passwd(realm, user, reload)
189
+ Digest::MD5::hexdigest([user, realm, self[user]].join(":"))
190
+ end
191
+
192
+ authenticator = WEBrick::HTTPAuth::DigestAuth.new(
193
+ :UserDB => userdb,
194
+ :Realm => REALM,
195
+ :Algorithm => "MD5"
196
+ )
197
+
198
+ @s.mount_proc("/") do |req,res|
199
+ authenticator.authenticate(req, res)
200
+ res.body = SECRET_DATA
201
+ end
202
+
203
+ one_shot
204
+
205
+ # no credentials
206
+ assert_raises(Atom::Unauthorized) { get_root }
207
+
208
+ @http.user = USER
209
+ @http.pass = PASS
210
+
211
+ # correct credentials
212
+ assert_authenticates
213
+
214
+ @s.stop
215
+ end
216
+
217
+ def test_wsse_auth
218
+ mount_one_shot do |req,res|
219
+ assert_equal 'WSSE profile="UsernameToken"', req["Authorization"]
220
+
221
+ xwsse = req["X-WSSE"]
222
+
223
+ p = @http.send :parse_quoted_wwwauth, xwsse
224
+
225
+ assert_equal USER, p[:Username]
226
+ assert_match /^UsernameToken /, xwsse
227
+
228
+ # Base64( SHA1( Nonce + CreationTimestamp + Password ) )
229
+ pd_string = p[:Nonce].unpack("m").first + p[:Created] + PASS
230
+ password_digest = [Digest::SHA1.digest(pd_string)].pack("m").chomp
231
+
232
+ assert_equal password_digest, p[:PasswordDigest]
233
+
234
+ res.body = SECRET_DATA
235
+ end
236
+
237
+ @http.always_auth = :wsse
238
+ @http.user = USER
239
+ @http.pass = PASS
240
+
241
+ assert_authenticates
242
+ end
243
+
244
+ def test_authsub_auth
245
+ mount_one_shot do |req,res|
246
+ assert_equal %{AuthSub token="#{TOKEN}"}, req["Authorization"]
247
+
248
+ res.body = SECRET_DATA
249
+ end
250
+
251
+ @http.always_auth = :authsub
252
+ @http.token = TOKEN
253
+
254
+ assert_authenticates
255
+ end
256
+
257
+ def test_multiple_auth
258
+ mount_one_shot do |req,res|
259
+ # WEBrick doesn't seem to support sending multiple headers, so this is the best we can do
260
+ res["WWW-Authenticate"] = %{NonexistantAuth parameter="yes", qop="auth", Basic realm="#{REALM}", something="true"}
261
+
262
+ if req["Authorization"]
263
+ res.body = SECRET_DATA
264
+ else
265
+ res.status = 401
266
+ end
267
+ end
268
+
269
+ @http.user = USER
270
+ @http.pass = PASS
271
+
272
+ assert_authenticates
273
+ end
274
+
275
+ # mount a block on the test server, shutting the server down after a
276
+ # single request
277
+ def mount_one_shot &block
278
+ @s.mount_proc("/") do |req,res|
279
+ block.call req, res
280
+ @s.stop
281
+ end
282
+
283
+ one_shot
284
+ end
285
+
286
+ # test that we authenticated properly
287
+ def assert_authenticates
288
+ get_root
289
+ assert_equal "200", @res.code
290
+ assert_equal SECRET_DATA, @res.body
291
+ end
292
+
293
+ # performs a GET on the test server
294
+ def get_root(*args)
295
+ @res = @http.get("http://localhost:#{@port}/", *args)
82
296
  end
83
297
 
84
- def get_root(*args); @http.get("http://localhost:#{@port}/", *args); end
298
+ # sets up the server for a single request
85
299
  def one_shot; Thread.new { @s.start }; end
86
300
  end
@@ -1,28 +1,47 @@
1
1
  require "test/unit"
2
2
 
3
- require "atom/app"
3
+ require "atom/service"
4
+
5
+ # needed for hpricot
6
+ require "rubygems"
4
7
 
5
8
  class FakeHTTP
6
9
  Response = Struct.new(:body, :code, :content_type)
7
10
 
8
- def initialize table, mime_type
11
+ def initialize table
9
12
  @table = table
10
- @mime_type = mime_type
11
13
  end
12
- def get url
14
+
15
+ def get url, headers = {}
13
16
  res = Response.new
14
- res.body = @table[url.to_s]
17
+
18
+ data = @table[url.to_s]
19
+
20
+ res.body = data[1]
15
21
  res.code = 200.to_s
16
- res.content_type = @mime_type
22
+ res.content_type = data[0]
23
+
24
+ def res.validate_content_type valid; valid.member? content_type; end
25
+
17
26
  res
18
27
  end
19
28
  end
20
29
 
21
30
  class AtomProtocolTest < Test::Unit::TestCase
22
- def test_introspection
31
+ attr_reader :have_hpricot
32
+
33
+ def initialize *args
34
+ super
35
+
36
+ require "hpricot"
37
+ @have_hpricot = true
38
+ rescue LoadError
39
+ puts "skipping hpricot tests"
40
+ end
23
41
 
24
- service = <<END
25
- <service xmlns="http://purl.org/atom/app#"
42
+ def test_introspection
43
+ doc = <<END
44
+ <service xmlns="http://www.w3.org/2007/app"
26
45
  xmlns:atom="http://www.w3.org/2005/Atom">
27
46
  <workspace>
28
47
  <atom:title>My Blog</atom:title>
@@ -36,23 +55,79 @@ class AtomProtocolTest < Test::Unit::TestCase
36
55
  </workspace>
37
56
  </service>
38
57
  END
39
-
40
- http = FakeHTTP.new({ "http://example.com/service.xml" => service }, "application/atomserv+xml")
41
58
 
59
+ service = Atom::Service.parse doc
42
60
 
43
- server = Atom::App.new "http://example.com/service.xml", http
61
+ ws = service.workspaces.first
62
+ assert_equal "My Blog", ws.title.to_s
44
63
 
45
- coll = server.collections.first
46
- assert_equal(URI.parse("http://example.org/myblog/entries"), coll.uri)
47
- assert_equal("Entries", coll.title.to_s)
48
- assert_equal("entry", coll.accepts)
64
+ coll = ws.collections.first
65
+ assert_equal "http://example.org/myblog/entries", coll.href
66
+ assert_equal "Entries", coll.title.to_s
67
+ assert_equal ["application/atom+xml;type=entry"], coll.accepts
49
68
 
50
- coll = server.collections.last
51
- assert_equal(URI.parse("http://example.org/myblog/fotes"), coll.uri)
52
- assert_equal("Photos", coll.title.to_s)
53
- assert_equal("image/*", coll.accepts)
69
+ coll = ws.collections.last
70
+ assert_equal "http://example.org/myblog/fotes", coll.href
71
+ assert_equal "Photos", coll.title.to_s
72
+ assert_equal ["image/*"], coll.accepts
54
73
 
55
- # XXX write a test for relative hrefs
74
+ http = service.instance_variable_get(:@http)
75
+ assert_instance_of Atom::HTTP, http
76
+ end
77
+
78
+ def test_write_introspection
79
+ service = Atom::Service.new
80
+
81
+ ws = service.workspaces.new
82
+
83
+ ws.title = "Workspace 1"
84
+
85
+ coll = Atom::Collection.new "http://example.org/entries"
86
+ coll.title = "Entries"
87
+ ws.collections << coll
88
+
89
+ coll = Atom::Collection.new "http://example.org/audio"
90
+ coll.title = "Audio"
91
+ coll.accepts = ["audio/*"]
92
+ ws.collections << coll
93
+
94
+ nses = { "app" => Atom::PP_NS, "atom" => Atom::NS }
95
+
96
+ doc = REXML::Document.new(service.to_s)
97
+
98
+ assert_equal "http://www.w3.org/2007/app", doc.root.namespace
99
+
100
+ ws = REXML::XPath.first( doc.root,
101
+ "/app:service/app:workspace",
102
+ nses )
103
+
104
+ title = REXML::XPath.first( ws, "./atom:title", nses)
105
+
106
+ assert_equal "Workspace 1", title.text
107
+ assert_equal "http://www.w3.org/2005/Atom", title.namespace
108
+
109
+ colls = REXML::XPath.match( ws, "./app:collection", nses)
110
+ assert_equal(2, colls.length)
111
+
112
+ entries = colls.first
113
+
114
+ assert_equal "http://example.org/entries", entries.attributes["href"]
115
+
116
+ title = REXML::XPath.first(entries, "./atom:title", nses)
117
+ assert_equal "Entries", title.text
118
+
119
+ accepts = REXML::XPath.first(entries, "./app:accept", nses)
120
+ assert_nil accepts
121
+
122
+ audio = colls.last
123
+
124
+ assert_equal "http://example.org/audio", audio.attributes["href"]
125
+
126
+ title = REXML::XPath.first(audio, "./atom:title", nses)
127
+ assert_equal "Audio", title.text
128
+
129
+ accepts = REXML::XPath.first(audio, "./app:accept", nses)
130
+ assert_equal "audio/*", accepts.text
56
131
  end
57
132
 
58
133
  def test_dont_specify_http_object
@@ -61,9 +136,53 @@ END
61
136
  assert_instance_of Atom::HTTP, collection.instance_variable_get("@http")
62
137
  end
63
138
 
64
- def test_collection_properly_inherits_feed
65
- collection = Atom::Collection.new("http://necronomicorp.com/testatom?atom")
139
+ def test_autodiscover_service_link
140
+ return unless have_hpricot
141
+
142
+ http = FakeHTTP.new \
143
+ 'http://example.org/' => [ 'text/html', '<html><link rel="service" href="svc">' ],
144
+ 'http://example.org/xhtml' => [ 'text/html', '<html><head><link rel="service" href="svc"/></head></html>' ],
145
+ 'http://example.org/svc' => [ 'application/atomsvc+xml', '<service xmlns="http://www.w3.org/2007/app"/>' ]
146
+
147
+ svc = Atom::Service.discover 'http://example.org/', http
148
+ assert_instance_of Atom::Service, svc
149
+
150
+ svc = Atom::Service.discover 'http://example.org/xhtml', http
151
+ assert_instance_of Atom::Service, svc
152
+ end
153
+
154
+ def test_autodiscover_rsd
155
+ return unless have_hpricot
156
+
157
+ http = FakeHTTP.new \
158
+ 'http://example.org/' => [ 'text/html', '<html><link rel="EditURI" href="rsd">' ],
159
+ 'http://example.org/svc' => [ 'application/atomsvc+xml', '<service xmlns="http://www.w3.org/2007/app"/>' ],
160
+ 'http://example.org/rsd' => [ 'text/xml', '<rsd version="1.0" xmlns="http://archipelago.phrasewise.com/rsd"><service><apis><api name="Atom" apiLink="svc" /></apis></service></rsd>' ]
161
+
162
+ svc = Atom::Service.discover 'http://example.org/', http
163
+ assert_instance_of Atom::Service, svc
164
+ end
165
+
166
+ def test_autodiscover_conneg
167
+ http = FakeHTTP.new \
168
+ 'http://example.org/svc' => [ 'application/atomsvc+xml', '<service xmlns="http://www.w3.org/2007/app"/>' ]
169
+
170
+ svc = Atom::Service.discover 'http://example.org/svc', http
171
+ assert_instance_of Atom::Service, svc
172
+ end
173
+
174
+ def test_cant_autodiscover
175
+ return unless have_hpricot
176
+
177
+ http = FakeHTTP.new 'http://example.org/h' => [ 'text/html', '<html>' ],
178
+ 'http://example.org/t' => [ 'text/plain', 'no joy.' ]
179
+
180
+ assert_raises Atom::AutodiscoveryFailure do
181
+ Atom::Service.discover 'http://example.org/h', http
182
+ end
66
183
 
67
- assert_equal [], collection.links
184
+ assert_raises Atom::AutodiscoveryFailure do
185
+ Atom::Service.discover 'http://example.org/t', http
186
+ end
68
187
  end
69
188
  end