3 from twisted.application import service
4 from twisted.trial import unittest
5 from twisted.internet import defer, reactor
6 from twisted.web import client, error, http
7 from twisted.python import failure, log
8 from allmydata import interfaces, provisioning, uri, webish
9 from allmydata.immutable import upload, download
10 from allmydata.web import status, common
11 from allmydata.util import fileutil, testutil
12 from allmydata.test.common import FakeDirectoryNode, FakeCHKFileNode, \
13 FakeMutableFileNode, create_chk_filenode
14 from allmydata.interfaces import IURI, INewDirectoryURI, \
15 IReadonlyNewDirectoryURI, IFileURI, IMutableFileURI, IMutableFileNode
16 from allmydata.mutable import servermap, publish, retrieve
18 # create a fake uploader/downloader, and a couple of fake dirnodes, then
19 # create a webserver that works against them
21 class FakeIntroducerClient:
22 def get_all_connectors(self):
24 def get_all_connections_for(self, service_name):
26 def get_all_peerids(self):
29 class FakeClient(service.MultiService):
30 nodeid = "fake_nodeid"
31 nickname = "fake_nickname"
32 basedir = "fake_basedir"
33 def get_versions(self):
34 return {'allmydata': "fake",
39 introducer_furl = "None"
40 introducer_client = FakeIntroducerClient()
41 _all_upload_status = [upload.UploadStatus()]
42 _all_download_status = [download.DownloadStatus()]
43 _all_mapupdate_statuses = [servermap.UpdateStatus()]
44 _all_publish_statuses = [publish.PublishStatus()]
45 _all_retrieve_statuses = [retrieve.RetrieveStatus()]
46 convergence = "some random string"
48 def connected_to_introducer(self):
51 def create_node_from_uri(self, auri):
52 u = uri.from_string(auri)
53 if (INewDirectoryURI.providedBy(u)
54 or IReadonlyNewDirectoryURI.providedBy(u)):
55 return FakeDirectoryNode(self).init_from_uri(u)
56 if IFileURI.providedBy(u):
57 return FakeCHKFileNode(u, self)
58 assert IMutableFileURI.providedBy(u), u
59 return FakeMutableFileNode(self).init_from_uri(u)
61 def create_empty_dirnode(self):
62 n = FakeDirectoryNode(self)
64 d.addCallback(lambda res: n)
67 MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT
68 def create_mutable_file(self, contents=""):
69 n = FakeMutableFileNode(self)
70 return n.create(contents)
72 def upload(self, uploadable):
73 d = uploadable.get_size()
74 d.addCallback(lambda size: uploadable.read(size))
77 n = create_chk_filenode(self, data)
78 results = upload.UploadResults()
79 results.uri = n.get_uri()
81 d.addCallback(_got_data)
84 def list_all_upload_statuses(self):
85 return self._all_upload_status
86 def list_all_download_statuses(self):
87 return self._all_download_status
88 def list_all_mapupdate_statuses(self):
89 return self._all_mapupdate_statuses
90 def list_all_publish_statuses(self):
91 return self._all_publish_statuses
92 def list_all_retrieve_statuses(self):
93 return self._all_retrieve_statuses
94 def list_all_helper_statuses(self):
97 class HTTPClientHEADFactory(client.HTTPClientFactory):
98 def __init__(self, *args, **kwargs):
99 client.HTTPClientFactory.__init__(self, *args, **kwargs)
100 self.deferred.addCallback(lambda res: self.response_headers)
102 def noPage(self, reason):
103 # Twisted-2.5.0 and earlier had a bug, in which they would raise an
104 # exception when the response to a HEAD request had no body (when in
105 # fact they are defined to never have a body). This was fixed in
106 # Twisted-8.0 . To work around this, we catch the
107 # PartialDownloadError and make it disappear.
108 if (reason.check(client.PartialDownloadError)
109 and self.method.upper() == "HEAD"):
112 return client.HTTPClientFactory.noPage(self, reason)
115 class WebMixin(object):
117 self.s = FakeClient()
118 self.s.startService()
119 self.ws = s = webish.WebishServer("0")
120 s.setServiceParent(self.s)
121 self.webish_port = port = s.listener._port.getHost().port
122 self.webish_url = "http://localhost:%d" % port
124 l = [ self.s.create_empty_dirnode() for x in range(6) ]
125 d = defer.DeferredList(l)
127 self.public_root = res[0][1]
128 assert interfaces.IDirectoryNode.providedBy(self.public_root), res
129 self.public_url = "/uri/" + self.public_root.get_uri()
130 self.private_root = res[1][1]
134 self._foo_uri = foo.get_uri()
135 self._foo_readonly_uri = foo.get_readonly_uri()
136 # NOTE: we ignore the deferred on all set_uri() calls, because we
137 # know the fake nodes do these synchronously
138 self.public_root.set_uri(u"foo", foo.get_uri())
140 self.BAR_CONTENTS, n, self._bar_txt_uri = self.makefile(0)
141 foo.set_uri(u"bar.txt", self._bar_txt_uri)
143 foo.set_uri(u"empty", res[3][1].get_uri())
144 sub_uri = res[4][1].get_uri()
145 self._sub_uri = sub_uri
146 foo.set_uri(u"sub", sub_uri)
147 sub = self.s.create_node_from_uri(sub_uri)
149 _ign, n, blocking_uri = self.makefile(1)
150 foo.set_uri(u"blockingfile", blocking_uri)
152 unicode_filename = u"n\u00fc.txt" # n u-umlaut . t x t
153 # ok, unicode calls it LATIN SMALL LETTER U WITH DIAERESIS but I
154 # still think of it as an umlaut
155 foo.set_uri(unicode_filename, self._bar_txt_uri)
157 _ign, n, baz_file = self.makefile(2)
158 sub.set_uri(u"baz.txt", baz_file)
160 _ign, n, self._bad_file_uri = self.makefile(3)
161 # this uri should not be downloadable
162 del FakeCHKFileNode.all_contents[self._bad_file_uri]
165 self.public_root.set_uri(u"reedownlee", rodir.get_readonly_uri())
166 rodir.set_uri(u"nor", baz_file)
171 # public/foo/blockingfile
174 # public/foo/sub/baz.txt
176 # public/reedownlee/nor
177 self.NEWFILE_CONTENTS = "newfile contents\n"
179 return foo.get_metadata_for(u"bar.txt")
181 def _got_metadata(metadata):
182 self._bar_txt_metadata = metadata
183 d.addCallback(_got_metadata)
186 def makefile(self, number):
187 contents = "contents of file %s\n" % number
188 n = create_chk_filenode(self.s, contents)
189 return contents, n, n.get_uri()
192 return self.s.stopService()
194 def failUnlessIsBarDotTxt(self, res):
195 self.failUnlessEqual(res, self.BAR_CONTENTS, res)
197 def failUnlessIsBarJSON(self, res):
198 data = simplejson.loads(res)
199 self.failUnless(isinstance(data, list))
200 self.failUnlessEqual(data[0], u"filenode")
201 self.failUnless(isinstance(data[1], dict))
202 self.failIf(data[1]["mutable"])
203 self.failIf("rw_uri" in data[1]) # immutable
204 self.failUnlessEqual(data[1]["ro_uri"], self._bar_txt_uri)
205 self.failUnlessEqual(data[1]["size"], len(self.BAR_CONTENTS))
207 def failUnlessIsFooJSON(self, res):
208 data = simplejson.loads(res)
209 self.failUnless(isinstance(data, list))
210 self.failUnlessEqual(data[0], "dirnode", res)
211 self.failUnless(isinstance(data[1], dict))
212 self.failUnless(data[1]["mutable"])
213 self.failUnless("rw_uri" in data[1]) # mutable
214 self.failUnlessEqual(data[1]["rw_uri"], self._foo_uri)
215 self.failUnlessEqual(data[1]["ro_uri"], self._foo_readonly_uri)
217 kidnames = sorted([unicode(n) for n in data[1]["children"]])
218 self.failUnlessEqual(kidnames,
219 [u"bar.txt", u"blockingfile", u"empty",
220 u"n\u00fc.txt", u"sub"])
221 kids = dict( [(unicode(name),value)
223 in data[1]["children"].iteritems()] )
224 self.failUnlessEqual(kids[u"sub"][0], "dirnode")
225 self.failUnless("metadata" in kids[u"sub"][1])
226 self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
227 self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
228 self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
229 self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
230 self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
231 self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
232 self._bar_txt_metadata["ctime"])
233 self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
236 def GET(self, urlpath, followRedirect=False):
237 assert not isinstance(urlpath, unicode)
238 url = self.webish_url + urlpath
239 return client.getPage(url, method="GET", followRedirect=followRedirect)
241 def HEAD(self, urlpath):
242 # this requires some surgery, because twisted.web.client doesn't want
243 # to give us back the response headers.
244 factory = HTTPClientHEADFactory(urlpath, method="HEAD")
245 reactor.connectTCP("localhost", self.webish_port, factory)
246 return factory.deferred
248 def PUT(self, urlpath, data):
249 url = self.webish_url + urlpath
250 return client.getPage(url, method="PUT", postdata=data)
252 def DELETE(self, urlpath):
253 url = self.webish_url + urlpath
254 return client.getPage(url, method="DELETE")
256 def POST(self, urlpath, followRedirect=False, **fields):
257 url = self.webish_url + urlpath
258 sepbase = "boogabooga"
262 form.append('Content-Disposition: form-data; name="_charset"')
266 for name, value in fields.iteritems():
267 if isinstance(value, tuple):
268 filename, value = value
269 form.append('Content-Disposition: form-data; name="%s"; '
270 'filename="%s"' % (name, filename.encode("utf-8")))
272 form.append('Content-Disposition: form-data; name="%s"' % name)
274 if isinstance(value, unicode):
275 value = value.encode("utf-8")
278 assert isinstance(value, str)
282 body = "\r\n".join(form) + "\r\n"
283 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
285 return client.getPage(url, method="POST", postdata=body,
286 headers=headers, followRedirect=followRedirect)
288 def shouldFail(self, res, expected_failure, which,
289 substring=None, response_substring=None):
290 if isinstance(res, failure.Failure):
291 res.trap(expected_failure)
293 self.failUnless(substring in str(res),
294 "substring '%s' not in '%s'"
295 % (substring, str(res)))
296 if response_substring:
297 self.failUnless(response_substring in res.value.response,
298 "response substring '%s' not in '%s'"
299 % (response_substring, res.value.response))
301 self.fail("%s was supposed to raise %s, not get '%s'" %
302 (which, expected_failure, res))
304 def shouldFail2(self, expected_failure, which, substring,
306 callable, *args, **kwargs):
307 assert substring is None or isinstance(substring, str)
308 assert response_substring is None or isinstance(response_substring, str)
309 d = defer.maybeDeferred(callable, *args, **kwargs)
311 if isinstance(res, failure.Failure):
312 res.trap(expected_failure)
314 self.failUnless(substring in str(res),
315 "%s: substring '%s' not in '%s'"
316 % (which, substring, str(res)))
317 if response_substring:
318 self.failUnless(response_substring in res.value.response,
319 "%s: response substring '%s' not in '%s'"
321 response_substring, res.value.response))
323 self.fail("%s was supposed to raise %s, not get '%s'" %
324 (which, expected_failure, res))
328 def should404(self, res, which):
329 if isinstance(res, failure.Failure):
330 res.trap(error.Error)
331 self.failUnlessEqual(res.value.status, "404")
333 self.fail("%s was supposed to Error(404), not get '%s'" %
336 def shouldHTTPError(self, res, which, code=None, substring=None,
337 response_substring=None):
338 if isinstance(res, failure.Failure):
339 res.trap(error.Error)
341 self.failUnlessEqual(res.value.status, str(code))
343 self.failUnless(substring in str(res),
344 "substring '%s' not in '%s'"
345 % (substring, str(res)))
346 if response_substring:
347 self.failUnless(response_substring in res.value.response,
348 "response substring '%s' not in '%s'"
349 % (response_substring, res.value.response))
351 self.fail("%s was supposed to Error(%s), not get '%s'" %
354 def shouldHTTPError2(self, which,
355 code=None, substring=None, response_substring=None,
356 callable=None, *args, **kwargs):
357 assert substring is None or isinstance(substring, str)
359 d = defer.maybeDeferred(callable, *args, **kwargs)
360 d.addBoth(self.shouldHTTPError, which,
361 code, substring, response_substring)
365 class Web(WebMixin, testutil.StallMixin, unittest.TestCase):
366 def test_create(self):
369 def test_welcome(self):
372 self.failUnless('Welcome To AllMyData' in res)
373 self.failUnless('Tahoe' in res)
375 self.s.basedir = 'web/test_welcome'
376 fileutil.make_dirs("web/test_welcome")
377 fileutil.make_dirs("web/test_welcome/private")
379 d.addCallback(_check)
382 def test_provisioning_math(self):
383 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
384 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
385 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
386 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
387 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
389 def test_provisioning(self):
390 d = self.GET("/provisioning/")
392 self.failUnless('Tahoe Provisioning Tool' in res)
393 fields = {'filled': True,
394 "num_users": int(50e3),
395 "files_per_user": 1000,
396 "space_per_user": int(1e9),
397 "sharing_ratio": 1.0,
398 "encoding_parameters": "3-of-10-5",
400 "ownership_mode": "A",
401 "download_rate": 100,
406 return self.POST("/provisioning/", **fields)
408 d.addCallback(_check)
410 self.failUnless('Tahoe Provisioning Tool' in res)
411 self.failUnless("Share space consumed: 167.01TB" in res)
413 fields = {'filled': True,
414 "num_users": int(50e6),
415 "files_per_user": 1000,
416 "space_per_user": int(5e9),
417 "sharing_ratio": 1.0,
418 "encoding_parameters": "25-of-100-50",
419 "num_servers": 30000,
420 "ownership_mode": "E",
421 "drive_failure_model": "U",
423 "download_rate": 1000,
428 return self.POST("/provisioning/", **fields)
429 d.addCallback(_check2)
431 self.failUnless("Share space consumed: huge!" in res)
432 fields = {'filled': True}
433 return self.POST("/provisioning/", **fields)
434 d.addCallback(_check3)
436 self.failUnless("Share space consumed:" in res)
437 d.addCallback(_check4)
440 def test_status(self):
441 dl_num = self.s.list_all_download_statuses()[0].get_counter()
442 ul_num = self.s.list_all_upload_statuses()[0].get_counter()
443 mu_num = self.s.list_all_mapupdate_statuses()[0].get_counter()
444 pub_num = self.s.list_all_publish_statuses()[0].get_counter()
445 ret_num = self.s.list_all_retrieve_statuses()[0].get_counter()
446 d = self.GET("/status", followRedirect=True)
448 self.failUnless('Upload and Download Status' in res, res)
449 self.failUnless('"down-%d"' % dl_num in res, res)
450 self.failUnless('"up-%d"' % ul_num in res, res)
451 self.failUnless('"mapupdate-%d"' % mu_num in res, res)
452 self.failUnless('"publish-%d"' % pub_num in res, res)
453 self.failUnless('"retrieve-%d"' % ret_num in res, res)
454 d.addCallback(_check)
455 d.addCallback(lambda res: self.GET("/status/?t=json"))
456 def _check_json(res):
457 data = simplejson.loads(res)
458 self.failUnless(isinstance(data, dict))
459 active = data["active"]
460 # TODO: test more. We need a way to fake an active operation
462 d.addCallback(_check_json)
464 d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num))
466 self.failUnless("File Download Status" in res, res)
467 d.addCallback(_check_dl)
468 d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
470 self.failUnless("File Upload Status" in res, res)
471 d.addCallback(_check_ul)
472 d.addCallback(lambda res: self.GET("/status/mapupdate-%d" % mu_num))
473 def _check_mapupdate(res):
474 self.failUnless("Mutable File Servermap Update Status" in res, res)
475 d.addCallback(_check_mapupdate)
476 d.addCallback(lambda res: self.GET("/status/publish-%d" % pub_num))
477 def _check_publish(res):
478 self.failUnless("Mutable File Publish Status" in res, res)
479 d.addCallback(_check_publish)
480 d.addCallback(lambda res: self.GET("/status/retrieve-%d" % ret_num))
481 def _check_retrieve(res):
482 self.failUnless("Mutable File Retrieve Status" in res, res)
483 d.addCallback(_check_retrieve)
487 def test_status_numbers(self):
488 drrm = status.DownloadResultsRendererMixin()
489 self.failUnlessEqual(drrm.render_time(None, None), "")
490 self.failUnlessEqual(drrm.render_time(None, 2.5), "2.50s")
491 self.failUnlessEqual(drrm.render_time(None, 0.25), "250ms")
492 self.failUnlessEqual(drrm.render_time(None, 0.0021), "2.1ms")
493 self.failUnlessEqual(drrm.render_time(None, 0.000123), "123us")
494 self.failUnlessEqual(drrm.render_rate(None, None), "")
495 self.failUnlessEqual(drrm.render_rate(None, 2500000), "2.50MBps")
496 self.failUnlessEqual(drrm.render_rate(None, 30100), "30.1kBps")
497 self.failUnlessEqual(drrm.render_rate(None, 123), "123Bps")
499 urrm = status.UploadResultsRendererMixin()
500 self.failUnlessEqual(urrm.render_time(None, None), "")
501 self.failUnlessEqual(urrm.render_time(None, 2.5), "2.50s")
502 self.failUnlessEqual(urrm.render_time(None, 0.25), "250ms")
503 self.failUnlessEqual(urrm.render_time(None, 0.0021), "2.1ms")
504 self.failUnlessEqual(urrm.render_time(None, 0.000123), "123us")
505 self.failUnlessEqual(urrm.render_rate(None, None), "")
506 self.failUnlessEqual(urrm.render_rate(None, 2500000), "2.50MBps")
507 self.failUnlessEqual(urrm.render_rate(None, 30100), "30.1kBps")
508 self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps")
510 def test_GET_FILEURL(self):
511 d = self.GET(self.public_url + "/foo/bar.txt")
512 d.addCallback(self.failUnlessIsBarDotTxt)
515 def test_HEAD_FILEURL(self):
516 d = self.HEAD(self.public_url + "/foo/bar.txt")
518 self.failUnlessEqual(headers["content-length"][0],
519 str(len(self.BAR_CONTENTS)))
520 self.failUnlessEqual(headers["content-type"], ["text/plain"])
524 def test_GET_FILEURL_named(self):
525 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
526 base2 = "/named/%s" % urllib.quote(self._bar_txt_uri)
527 d = self.GET(base + "/@@name=/blah.txt")
528 d.addCallback(self.failUnlessIsBarDotTxt)
529 d.addCallback(lambda res: self.GET(base + "/blah.txt"))
530 d.addCallback(self.failUnlessIsBarDotTxt)
531 d.addCallback(lambda res: self.GET(base + "/ignore/lots/blah.txt"))
532 d.addCallback(self.failUnlessIsBarDotTxt)
533 d.addCallback(lambda res: self.GET(base2 + "/@@name=/blah.txt"))
534 d.addCallback(self.failUnlessIsBarDotTxt)
535 save_url = base + "?save=true&filename=blah.txt"
536 d.addCallback(lambda res: self.GET(save_url))
537 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
538 u_filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
539 u_fn_e = urllib.quote(u_filename.encode("utf-8"))
540 u_url = base + "?save=true&filename=" + u_fn_e
541 d.addCallback(lambda res: self.GET(u_url))
542 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
545 def test_PUT_FILEURL_named_bad(self):
546 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
547 d = self.shouldFail2(error.Error, "test_PUT_FILEURL_named_bad",
549 "/file can only be used with GET or HEAD",
550 self.PUT, base + "/@@name=/blah.txt", "")
553 def test_GET_DIRURL_named_bad(self):
554 base = "/file/%s" % urllib.quote(self._foo_uri)
555 d = self.shouldFail2(error.Error, "test_PUT_DIRURL_named_bad",
558 self.GET, base + "/@@name=/blah.txt")
561 def test_GET_slash_file_bad(self):
562 d = self.shouldFail2(error.Error, "test_GET_slash_file_bad",
564 "/file must be followed by a file-cap and a name",
568 def test_GET_unhandled_URI_named(self):
569 contents, n, newuri = self.makefile(12)
570 verifier_cap = n.get_verifier().to_string()
571 base = "/file/%s" % urllib.quote(verifier_cap)
572 # client.create_node_from_uri() can't handle verify-caps
573 d = self.shouldFail2(error.Error, "GET_unhandled_URI_named",
575 "is not a valid file- or directory- cap",
579 def test_GET_unhandled_URI(self):
580 contents, n, newuri = self.makefile(12)
581 verifier_cap = n.get_verifier().to_string()
582 base = "/uri/%s" % urllib.quote(verifier_cap)
583 # client.create_node_from_uri() can't handle verify-caps
584 d = self.shouldFail2(error.Error, "test_GET_unhandled_URI",
586 "is not a valid file- or directory- cap",
590 def test_GET_FILE_URI(self):
591 base = "/uri/%s" % urllib.quote(self._bar_txt_uri)
593 d.addCallback(self.failUnlessIsBarDotTxt)
596 def test_GET_FILE_URI_badchild(self):
597 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
598 errmsg = "Files have no children, certainly not named 'boguschild'"
599 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
600 "400 Bad Request", errmsg,
604 def test_PUT_FILE_URI_badchild(self):
605 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
606 errmsg = "Cannot create directory 'boguschild', because its parent is a file, not a directory"
607 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
608 "400 Bad Request", errmsg,
612 def test_GET_FILEURL_save(self):
613 d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true")
614 # TODO: look at the headers, expect a Content-Disposition: attachment
616 d.addCallback(self.failUnlessIsBarDotTxt)
619 def test_GET_FILEURL_missing(self):
620 d = self.GET(self.public_url + "/foo/missing")
621 d.addBoth(self.should404, "test_GET_FILEURL_missing")
624 def test_PUT_NEWFILEURL(self):
625 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
626 # TODO: we lose the response code, so we can't check this
627 #self.failUnlessEqual(responsecode, 201)
628 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
629 d.addCallback(lambda res:
630 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
631 self.NEWFILE_CONTENTS))
634 def test_PUT_NEWFILEURL_mutable(self):
635 d = self.PUT(self.public_url + "/foo/new.txt?mutable=true",
636 self.NEWFILE_CONTENTS)
637 # TODO: we lose the response code, so we can't check this
638 #self.failUnlessEqual(responsecode, 201)
640 u = uri.from_string_mutable_filenode(res)
641 self.failUnless(u.is_mutable())
642 self.failIf(u.is_readonly())
644 d.addCallback(_check_uri)
645 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
646 d.addCallback(lambda res:
647 self.failUnlessMutableChildContentsAre(self._foo_node,
649 self.NEWFILE_CONTENTS))
652 def test_PUT_NEWFILEURL_mutable_toobig(self):
653 d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig",
654 "413 Request Entity Too Large",
655 "SDMF is limited to one segment, and 10001 > 10000",
657 self.public_url + "/foo/new.txt?mutable=true",
658 "b" * (self.s.MUTABLE_SIZELIMIT+1))
661 def test_PUT_NEWFILEURL_replace(self):
662 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
663 # TODO: we lose the response code, so we can't check this
664 #self.failUnlessEqual(responsecode, 200)
665 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
666 d.addCallback(lambda res:
667 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
668 self.NEWFILE_CONTENTS))
671 def test_PUT_NEWFILEURL_bad_t(self):
672 d = self.shouldFail2(error.Error, "PUT_bad_t", "400 Bad Request",
673 "PUT to a file: bad t=bogus",
674 self.PUT, self.public_url + "/foo/bar.txt?t=bogus",
678 def test_PUT_NEWFILEURL_no_replace(self):
679 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
680 self.NEWFILE_CONTENTS)
681 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
683 "There was already a child by that name, and you asked me "
687 def test_PUT_NEWFILEURL_mkdirs(self):
688 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
690 d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
691 d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
692 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
693 d.addCallback(lambda res:
694 self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
695 self.NEWFILE_CONTENTS))
698 def test_PUT_NEWFILEURL_blocked(self):
699 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
700 self.NEWFILE_CONTENTS)
701 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
703 "Unable to create directory 'blockingfile': a file was in the way")
706 def test_DELETE_FILEURL(self):
707 d = self.DELETE(self.public_url + "/foo/bar.txt")
708 d.addCallback(lambda res:
709 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
712 def test_DELETE_FILEURL_missing(self):
713 d = self.DELETE(self.public_url + "/foo/missing")
714 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
717 def test_DELETE_FILEURL_missing2(self):
718 d = self.DELETE(self.public_url + "/missing/missing")
719 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
722 def test_GET_FILEURL_json(self):
723 # twisted.web.http.parse_qs ignores any query args without an '=', so
724 # I can't do "GET /path?json", I have to do "GET /path/t=json"
725 # instead. This may make it tricky to emulate the S3 interface
727 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
728 d.addCallback(self.failUnlessIsBarJSON)
731 def test_GET_FILEURL_json_missing(self):
732 d = self.GET(self.public_url + "/foo/missing?json")
733 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
736 def test_GET_FILEURL_uri(self):
737 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
739 self.failUnlessEqual(res, self._bar_txt_uri)
740 d.addCallback(_check)
741 d.addCallback(lambda res:
742 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
744 # for now, for files, uris and readonly-uris are the same
745 self.failUnlessEqual(res, self._bar_txt_uri)
746 d.addCallback(_check2)
749 def test_GET_FILEURL_badtype(self):
750 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
753 self.public_url + "/foo/bar.txt?t=bogus")
756 def test_GET_FILEURL_uri_missing(self):
757 d = self.GET(self.public_url + "/foo/missing?t=uri")
758 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
761 def test_GET_DIRURL(self):
762 # the addSlash means we get a redirect here
763 # from /uri/$URI/foo/ , we need ../../../ to get back to the root
765 d = self.GET(self.public_url + "/foo", followRedirect=True)
767 self.failUnless(('<a href="%s">Return to Welcome page' % ROOT)
769 # the FILE reference points to a URI, but it should end in bar.txt
770 bar_url = ("%s/file/%s/@@named=/bar.txt" %
771 (ROOT, urllib.quote(self._bar_txt_uri)))
772 get_bar = "".join([r'<td>',
773 r'<a href="%s">bar.txt</a>' % bar_url,
776 r'\s+<td>%d</td>' % len(self.BAR_CONTENTS),
778 self.failUnless(re.search(get_bar, res), res)
779 for line in res.split("\n"):
780 # find the line that contains the delete button for bar.txt
781 if ("form action" in line and
782 'value="delete"' in line and
783 'value="bar.txt"' in line):
784 # the form target should use a relative URL
785 foo_url = urllib.quote("%s/uri/%s/" % (ROOT, self._foo_uri))
786 self.failUnless(('action="%s"' % foo_url) in line, line)
787 # and the when_done= should too
788 #done_url = urllib.quote(???)
789 #self.failUnless(('name="when_done" value="%s"' % done_url)
793 self.fail("unable to find delete-bar.txt line", res)
795 # the DIR reference just points to a URI
796 sub_url = ("%s/uri/%s/" % (ROOT, urllib.quote(self._sub_uri)))
797 get_sub = ((r'<td><a href="%s">sub</a></td>' % sub_url)
798 + r'\s+<td>DIR</td>')
799 self.failUnless(re.search(get_sub, res), res)
800 d.addCallback(_check)
802 # look at a directory which is readonly
803 d.addCallback(lambda res:
804 self.GET(self.public_url + "/reedownlee", followRedirect=True))
806 self.failUnless("(readonly)" in res, res)
807 self.failIf("Upload a file" in res, res)
808 d.addCallback(_check2)
810 # and at a directory that contains a readonly directory
811 d.addCallback(lambda res:
812 self.GET(self.public_url, followRedirect=True))
814 self.failUnless(re.search(r'<td><a href="[\.\/]+/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
815 '</td>\s+<td>DIR-RO</td>', res))
816 d.addCallback(_check3)
820 def test_GET_DIRURL_badtype(self):
821 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
825 self.public_url + "/foo?t=bogus")
828 def test_GET_DIRURL_json(self):
829 d = self.GET(self.public_url + "/foo?t=json")
830 d.addCallback(self.failUnlessIsFooJSON)
834 def test_POST_DIRURL_manifest_no_ophandle(self):
835 d = self.shouldFail2(error.Error,
836 "test_POST_DIRURL_manifest_no_ophandle",
838 "slow operation requires ophandle=",
839 self.POST, self.public_url, t="start-manifest")
842 def test_POST_DIRURL_manifest(self):
843 d = defer.succeed(None)
844 def getman(ignored, output):
845 d = self.POST(self.public_url + "/foo/?t=start-manifest&ophandle=125",
847 d.addCallback(self.wait_for_operation, "125")
848 d.addCallback(self.get_operation_results, "125", output)
850 d.addCallback(getman, None)
851 def _got_html(manifest):
852 self.failUnless("Manifest of SI=" in manifest)
853 self.failUnless("<td>sub</td>" in manifest)
854 self.failUnless(self._sub_uri in manifest)
855 self.failUnless("<td>sub/baz.txt</td>" in manifest)
856 d.addCallback(_got_html)
857 d.addCallback(getman, "html")
858 d.addCallback(_got_html)
859 d.addCallback(getman, "text")
860 def _got_text(manifest):
861 self.failUnless("\nsub " + self._sub_uri + "\n" in manifest)
862 self.failUnless("\nsub/baz.txt URI:CHK:" in manifest)
863 d.addCallback(_got_text)
864 d.addCallback(getman, "JSON")
865 def _got_json(manifest):
866 data = manifest["manifest"]
868 for (path_list, cap) in data:
869 got[tuple(path_list)] = cap
870 self.failUnlessEqual(got[(u"sub",)], self._sub_uri)
871 self.failUnless((u"sub",u"baz.txt") in got)
872 d.addCallback(_got_json)
875 def test_POST_DIRURL_deepsize_no_ophandle(self):
876 d = self.shouldFail2(error.Error,
877 "test_POST_DIRURL_deepsize_no_ophandle",
879 "slow operation requires ophandle=",
880 self.POST, self.public_url, t="start-deep-size")
883 def test_POST_DIRURL_deepsize(self):
884 d = self.POST(self.public_url + "/foo/?t=start-deep-size&ophandle=126",
886 d.addCallback(self.wait_for_operation, "126")
887 d.addCallback(self.get_operation_results, "126", "json")
889 self.failUnlessEqual(data["finished"], True)
891 self.failUnless(size > 1000)
892 d.addCallback(_got_json)
893 d.addCallback(self.get_operation_results, "126", "text")
895 mo = re.search(r'^size: (\d+)$', res, re.M)
896 self.failUnless(mo, res)
897 size = int(mo.group(1))
898 # with directories, the size varies.
899 self.failUnless(size > 1000)
900 d.addCallback(_got_text)
903 def test_POST_DIRURL_deepstats_no_ophandle(self):
904 d = self.shouldFail2(error.Error,
905 "test_POST_DIRURL_deepstats_no_ophandle",
907 "slow operation requires ophandle=",
908 self.POST, self.public_url, t="start-deep-stats")
911 def test_POST_DIRURL_deepstats(self):
912 d = self.POST(self.public_url + "/foo/?t=start-deep-stats&ophandle=127",
914 d.addCallback(self.wait_for_operation, "127")
915 d.addCallback(self.get_operation_results, "127", "json")
916 def _got_json(stats):
917 expected = {"count-immutable-files": 3,
918 "count-mutable-files": 0,
919 "count-literal-files": 0,
921 "count-directories": 3,
922 "size-immutable-files": 57,
923 "size-literal-files": 0,
924 #"size-directories": 1912, # varies
925 #"largest-directory": 1590,
926 "largest-directory-children": 5,
927 "largest-immutable-file": 19,
929 for k,v in expected.iteritems():
930 self.failUnlessEqual(stats[k], v,
931 "stats[%s] was %s, not %s" %
933 self.failUnlessEqual(stats["size-files-histogram"],
935 d.addCallback(_got_json)
938 def test_GET_DIRURL_uri(self):
939 d = self.GET(self.public_url + "/foo?t=uri")
941 self.failUnlessEqual(res, self._foo_uri)
942 d.addCallback(_check)
945 def test_GET_DIRURL_readonly_uri(self):
946 d = self.GET(self.public_url + "/foo?t=readonly-uri")
948 self.failUnlessEqual(res, self._foo_readonly_uri)
949 d.addCallback(_check)
952 def test_PUT_NEWDIRURL(self):
953 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
954 d.addCallback(lambda res:
955 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
956 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
957 d.addCallback(self.failUnlessNodeKeysAre, [])
960 def test_PUT_NEWDIRURL_exists(self):
961 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
962 d.addCallback(lambda res:
963 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
964 d.addCallback(lambda res: self._foo_node.get(u"sub"))
965 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
968 def test_PUT_NEWDIRURL_blocked(self):
969 d = self.shouldFail2(error.Error, "PUT_NEWDIRURL_blocked",
970 "409 Conflict", "Unable to create directory 'bar.txt': a file was in the way",
972 self.public_url + "/foo/bar.txt/sub?t=mkdir", "")
973 d.addCallback(lambda res:
974 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
975 d.addCallback(lambda res: self._foo_node.get(u"sub"))
976 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
979 def test_PUT_NEWDIRURL_mkdir_p(self):
980 d = defer.succeed(None)
981 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp'))
982 d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp"))
983 d.addCallback(lambda res: self._foo_node.get(u"mkp"))
984 def mkdir_p(mkpnode):
985 url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri())
987 def made_subsub(ssuri):
988 d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2")
989 d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri))
991 d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri))
993 d.addCallback(made_subsub)
995 d.addCallback(mkdir_p)
998 def test_PUT_NEWDIRURL_mkdirs(self):
999 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
1000 d.addCallback(lambda res:
1001 self.failIfNodeHasChild(self._foo_node, u"newdir"))
1002 d.addCallback(lambda res:
1003 self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
1004 d.addCallback(lambda res:
1005 self._foo_node.get_child_at_path(u"subdir/newdir"))
1006 d.addCallback(self.failUnlessNodeKeysAre, [])
1009 def test_DELETE_DIRURL(self):
1010 d = self.DELETE(self.public_url + "/foo")
1011 d.addCallback(lambda res:
1012 self.failIfNodeHasChild(self.public_root, u"foo"))
1015 def test_DELETE_DIRURL_missing(self):
1016 d = self.DELETE(self.public_url + "/foo/missing")
1017 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
1018 d.addCallback(lambda res:
1019 self.failUnlessNodeHasChild(self.public_root, u"foo"))
1022 def test_DELETE_DIRURL_missing2(self):
1023 d = self.DELETE(self.public_url + "/missing")
1024 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
1027 def dump_root(self):
1029 w = webish.DirnodeWalkerMixin()
1030 def visitor(childpath, childnode, metadata):
1032 d = w.walk(self.public_root, visitor)
1035 def failUnlessNodeKeysAre(self, node, expected_keys):
1036 for k in expected_keys:
1037 assert isinstance(k, unicode)
1039 def _check(children):
1040 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
1041 d.addCallback(_check)
1043 def failUnlessNodeHasChild(self, node, name):
1044 assert isinstance(name, unicode)
1046 def _check(children):
1047 self.failUnless(name in children)
1048 d.addCallback(_check)
1050 def failIfNodeHasChild(self, node, name):
1051 assert isinstance(name, unicode)
1053 def _check(children):
1054 self.failIf(name in children)
1055 d.addCallback(_check)
1058 def failUnlessChildContentsAre(self, node, name, expected_contents):
1059 assert isinstance(name, unicode)
1060 d = node.get_child_at_path(name)
1061 d.addCallback(lambda node: node.download_to_data())
1062 def _check(contents):
1063 self.failUnlessEqual(contents, expected_contents)
1064 d.addCallback(_check)
1067 def failUnlessMutableChildContentsAre(self, node, name, expected_contents):
1068 assert isinstance(name, unicode)
1069 d = node.get_child_at_path(name)
1070 d.addCallback(lambda node: node.download_best_version())
1071 def _check(contents):
1072 self.failUnlessEqual(contents, expected_contents)
1073 d.addCallback(_check)
1076 def failUnlessChildURIIs(self, node, name, expected_uri):
1077 assert isinstance(name, unicode)
1078 d = node.get_child_at_path(name)
1080 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
1081 d.addCallback(_check)
1084 def failUnlessURIMatchesChild(self, got_uri, node, name):
1085 assert isinstance(name, unicode)
1086 d = node.get_child_at_path(name)
1088 self.failUnlessEqual(got_uri.strip(), child.get_uri())
1089 d.addCallback(_check)
1092 def failUnlessCHKURIHasContents(self, got_uri, contents):
1093 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
1095 def test_POST_upload(self):
1096 d = self.POST(self.public_url + "/foo", t="upload",
1097 file=("new.txt", self.NEWFILE_CONTENTS))
1099 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1100 d.addCallback(lambda res:
1101 self.failUnlessChildContentsAre(fn, u"new.txt",
1102 self.NEWFILE_CONTENTS))
1105 def test_POST_upload_unicode(self):
1106 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1107 d = self.POST(self.public_url + "/foo", t="upload",
1108 file=(filename, self.NEWFILE_CONTENTS))
1110 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1111 d.addCallback(lambda res:
1112 self.failUnlessChildContentsAre(fn, filename,
1113 self.NEWFILE_CONTENTS))
1114 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1115 d.addCallback(lambda res: self.GET(target_url))
1116 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1117 self.NEWFILE_CONTENTS,
1121 def test_POST_upload_unicode_named(self):
1122 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1123 d = self.POST(self.public_url + "/foo", t="upload",
1125 file=("overridden", self.NEWFILE_CONTENTS))
1127 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1128 d.addCallback(lambda res:
1129 self.failUnlessChildContentsAre(fn, filename,
1130 self.NEWFILE_CONTENTS))
1131 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1132 d.addCallback(lambda res: self.GET(target_url))
1133 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1134 self.NEWFILE_CONTENTS,
1138 def test_POST_upload_no_link(self):
1139 d = self.POST("/uri", t="upload",
1140 file=("new.txt", self.NEWFILE_CONTENTS))
1141 def _check_upload_results(page):
1142 # this should be a page which describes the results of the upload
1143 # that just finished.
1144 self.failUnless("Upload Results:" in page)
1145 self.failUnless("URI:" in page)
1146 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1147 mo = uri_re.search(page)
1148 self.failUnless(mo, page)
1149 new_uri = mo.group(1)
1151 d.addCallback(_check_upload_results)
1152 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1155 def test_POST_upload_no_link_whendone(self):
1156 d = self.POST("/uri", t="upload", when_done="/",
1157 file=("new.txt", self.NEWFILE_CONTENTS))
1158 d.addBoth(self.shouldRedirect, "/")
1161 def shouldRedirect2(self, which, checker, callable, *args, **kwargs):
1162 d = defer.maybeDeferred(callable, *args, **kwargs)
1164 if isinstance(res, failure.Failure):
1165 res.trap(error.PageRedirect)
1166 statuscode = res.value.status
1167 target = res.value.location
1168 return checker(statuscode, target)
1169 self.fail("%s: callable was supposed to redirect, not return '%s'"
1174 def test_POST_upload_no_link_whendone_results(self):
1175 def check(statuscode, target):
1176 self.failUnlessEqual(statuscode, str(http.FOUND))
1177 self.failUnless(target.startswith(self.webish_url), target)
1178 return client.getPage(target, method="GET")
1179 d = self.shouldRedirect2("test_POST_upload_no_link_whendone_results",
1181 self.POST, "/uri", t="upload",
1182 when_done="/uri/%(uri)s",
1183 file=("new.txt", self.NEWFILE_CONTENTS))
1184 d.addCallback(lambda res:
1185 self.failUnlessEqual(res, self.NEWFILE_CONTENTS))
1188 def test_POST_upload_no_link_mutable(self):
1189 d = self.POST("/uri", t="upload", mutable="true",
1190 file=("new.txt", self.NEWFILE_CONTENTS))
1191 def _check(new_uri):
1192 new_uri = new_uri.strip()
1193 self.new_uri = new_uri
1195 self.failUnless(IMutableFileURI.providedBy(u))
1196 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1197 n = self.s.create_node_from_uri(new_uri)
1198 return n.download_best_version()
1199 d.addCallback(_check)
1201 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1202 return self.GET("/uri/%s" % urllib.quote(self.new_uri))
1203 d.addCallback(_check2)
1205 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1206 return self.GET("/file/%s" % urllib.quote(self.new_uri))
1207 d.addCallback(_check3)
1209 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1210 d.addCallback(_check4)
1213 def test_POST_upload_no_link_mutable_toobig(self):
1214 d = self.shouldFail2(error.Error,
1215 "test_POST_upload_no_link_mutable_toobig",
1216 "413 Request Entity Too Large",
1217 "SDMF is limited to one segment, and 10001 > 10000",
1219 "/uri", t="upload", mutable="true",
1221 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1224 def test_POST_upload_mutable(self):
1225 # this creates a mutable file
1226 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1227 file=("new.txt", self.NEWFILE_CONTENTS))
1229 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1230 d.addCallback(lambda res:
1231 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1232 self.NEWFILE_CONTENTS))
1233 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1235 self.failUnless(IMutableFileNode.providedBy(newnode))
1236 self.failUnless(newnode.is_mutable())
1237 self.failIf(newnode.is_readonly())
1238 self._mutable_node = newnode
1239 self._mutable_uri = newnode.get_uri()
1242 # now upload it again and make sure that the URI doesn't change
1243 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1244 d.addCallback(lambda res:
1245 self.POST(self.public_url + "/foo", t="upload",
1247 file=("new.txt", NEWER_CONTENTS)))
1248 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1249 d.addCallback(lambda res:
1250 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1252 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1254 self.failUnless(IMutableFileNode.providedBy(newnode))
1255 self.failUnless(newnode.is_mutable())
1256 self.failIf(newnode.is_readonly())
1257 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1258 d.addCallback(_got2)
1260 # upload a second time, using PUT instead of POST
1261 NEW2_CONTENTS = NEWER_CONTENTS + "overwrite with PUT\n"
1262 d.addCallback(lambda res:
1263 self.PUT(self.public_url + "/foo/new.txt", NEW2_CONTENTS))
1264 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1265 d.addCallback(lambda res:
1266 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1269 # finally list the directory, since mutable files are displayed
1270 # slightly differently
1272 d.addCallback(lambda res:
1273 self.GET(self.public_url + "/foo/",
1274 followRedirect=True))
1275 def _check_page(res):
1276 # TODO: assert more about the contents
1277 self.failUnless("SSK" in res)
1279 d.addCallback(_check_page)
1281 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1283 self.failUnless(IMutableFileNode.providedBy(newnode))
1284 self.failUnless(newnode.is_mutable())
1285 self.failIf(newnode.is_readonly())
1286 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1287 d.addCallback(_got3)
1289 # look at the JSON form of the enclosing directory
1290 d.addCallback(lambda res:
1291 self.GET(self.public_url + "/foo/?t=json",
1292 followRedirect=True))
1293 def _check_page_json(res):
1294 parsed = simplejson.loads(res)
1295 self.failUnlessEqual(parsed[0], "dirnode")
1296 children = dict( [(unicode(name),value)
1298 in parsed[1]["children"].iteritems()] )
1299 self.failUnless("new.txt" in children)
1300 new_json = children["new.txt"]
1301 self.failUnlessEqual(new_json[0], "filenode")
1302 self.failUnless(new_json[1]["mutable"])
1303 self.failUnlessEqual(new_json[1]["rw_uri"], self._mutable_uri)
1304 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1305 self.failUnlessEqual(new_json[1]["ro_uri"], ro_uri)
1306 d.addCallback(_check_page_json)
1308 # and the JSON form of the file
1309 d.addCallback(lambda res:
1310 self.GET(self.public_url + "/foo/new.txt?t=json"))
1311 def _check_file_json(res):
1312 parsed = simplejson.loads(res)
1313 self.failUnlessEqual(parsed[0], "filenode")
1314 self.failUnless(parsed[1]["mutable"])
1315 self.failUnlessEqual(parsed[1]["rw_uri"], self._mutable_uri)
1316 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1317 self.failUnlessEqual(parsed[1]["ro_uri"], ro_uri)
1318 d.addCallback(_check_file_json)
1320 # and look at t=uri and t=readonly-uri
1321 d.addCallback(lambda res:
1322 self.GET(self.public_url + "/foo/new.txt?t=uri"))
1323 d.addCallback(lambda res: self.failUnlessEqual(res, self._mutable_uri))
1324 d.addCallback(lambda res:
1325 self.GET(self.public_url + "/foo/new.txt?t=readonly-uri"))
1326 def _check_ro_uri(res):
1327 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1328 self.failUnlessEqual(res, ro_uri)
1329 d.addCallback(_check_ro_uri)
1331 # make sure we can get to it from /uri/URI
1332 d.addCallback(lambda res:
1333 self.GET("/uri/%s" % urllib.quote(self._mutable_uri)))
1334 d.addCallback(lambda res:
1335 self.failUnlessEqual(res, NEW2_CONTENTS))
1337 # and that HEAD computes the size correctly
1338 d.addCallback(lambda res:
1339 self.HEAD(self.public_url + "/foo/new.txt"))
1340 def _got_headers(headers):
1341 self.failUnlessEqual(headers["content-length"][0],
1342 str(len(NEW2_CONTENTS)))
1343 self.failUnlessEqual(headers["content-type"], ["text/plain"])
1344 d.addCallback(_got_headers)
1346 # make sure that size errors are displayed correctly for overwrite
1347 d.addCallback(lambda res:
1348 self.shouldFail2(error.Error,
1349 "test_POST_upload_mutable-toobig",
1350 "413 Request Entity Too Large",
1351 "SDMF is limited to one segment, and 10001 > 10000",
1353 self.public_url + "/foo", t="upload",
1356 "b" * (self.s.MUTABLE_SIZELIMIT+1)),
1359 d.addErrback(self.dump_error)
1362 def test_POST_upload_mutable_toobig(self):
1363 d = self.shouldFail2(error.Error,
1364 "test_POST_upload_no_link_mutable_toobig",
1365 "413 Request Entity Too Large",
1366 "SDMF is limited to one segment, and 10001 > 10000",
1368 self.public_url + "/foo",
1369 t="upload", mutable="true",
1371 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1374 def dump_error(self, f):
1375 # if the web server returns an error code (like 400 Bad Request),
1376 # web.client.getPage puts the HTTP response body into the .response
1377 # attribute of the exception object that it gives back. It does not
1378 # appear in the Failure's repr(), so the ERROR that trial displays
1379 # will be rather terse and unhelpful. addErrback this method to the
1380 # end of your chain to get more information out of these errors.
1381 if f.check(error.Error):
1382 print "web.error.Error:"
1384 print f.value.response
1387 def test_POST_upload_replace(self):
1388 d = self.POST(self.public_url + "/foo", t="upload",
1389 file=("bar.txt", self.NEWFILE_CONTENTS))
1391 d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
1392 d.addCallback(lambda res:
1393 self.failUnlessChildContentsAre(fn, u"bar.txt",
1394 self.NEWFILE_CONTENTS))
1397 def test_POST_upload_no_replace_ok(self):
1398 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1399 file=("new.txt", self.NEWFILE_CONTENTS))
1400 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1401 d.addCallback(lambda res: self.failUnlessEqual(res,
1402 self.NEWFILE_CONTENTS))
1405 def test_POST_upload_no_replace_queryarg(self):
1406 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1407 file=("bar.txt", self.NEWFILE_CONTENTS))
1408 d.addBoth(self.shouldFail, error.Error,
1409 "POST_upload_no_replace_queryarg",
1411 "There was already a child by that name, and you asked me "
1412 "to not replace it")
1413 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1414 d.addCallback(self.failUnlessIsBarDotTxt)
1417 def test_POST_upload_no_replace_field(self):
1418 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1419 file=("bar.txt", self.NEWFILE_CONTENTS))
1420 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1422 "There was already a child by that name, and you asked me "
1423 "to not replace it")
1424 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1425 d.addCallback(self.failUnlessIsBarDotTxt)
1428 def test_POST_upload_whendone(self):
1429 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1430 file=("new.txt", self.NEWFILE_CONTENTS))
1431 d.addBoth(self.shouldRedirect, "/THERE")
1433 d.addCallback(lambda res:
1434 self.failUnlessChildContentsAre(fn, u"new.txt",
1435 self.NEWFILE_CONTENTS))
1438 def test_POST_upload_named(self):
1440 d = self.POST(self.public_url + "/foo", t="upload",
1441 name="new.txt", file=self.NEWFILE_CONTENTS)
1442 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1443 d.addCallback(lambda res:
1444 self.failUnlessChildContentsAre(fn, u"new.txt",
1445 self.NEWFILE_CONTENTS))
1448 def test_POST_upload_named_badfilename(self):
1449 d = self.POST(self.public_url + "/foo", t="upload",
1450 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1451 d.addBoth(self.shouldFail, error.Error,
1452 "test_POST_upload_named_badfilename",
1454 "name= may not contain a slash",
1456 # make sure that nothing was added
1457 d.addCallback(lambda res:
1458 self.failUnlessNodeKeysAre(self._foo_node,
1459 [u"bar.txt", u"blockingfile",
1460 u"empty", u"n\u00fc.txt",
1464 def test_POST_FILEURL_check(self):
1465 bar_url = self.public_url + "/foo/bar.txt"
1466 d = self.POST(bar_url, t="check")
1468 self.failUnless("Healthy!" in res)
1469 d.addCallback(_check)
1470 redir_url = "http://allmydata.org/TARGET"
1471 def _check2(statuscode, target):
1472 self.failUnlessEqual(statuscode, str(http.FOUND))
1473 self.failUnlessEqual(target, redir_url)
1474 d.addCallback(lambda res:
1475 self.shouldRedirect2("test_POST_FILEURL_check",
1479 when_done=redir_url))
1480 d.addCallback(lambda res:
1481 self.POST(bar_url, t="check", return_to=redir_url))
1483 self.failUnless("Healthy!" in res)
1484 self.failUnless("Return to parent directory" in res)
1485 self.failUnless(redir_url in res)
1486 d.addCallback(_check3)
1489 def test_POST_FILEURL_check_and_repair(self):
1490 bar_url = self.public_url + "/foo/bar.txt"
1491 d = self.POST(bar_url, t="check", repair="true")
1493 self.failUnless("Healthy!" in res)
1494 d.addCallback(_check)
1495 redir_url = "http://allmydata.org/TARGET"
1496 def _check2(statuscode, target):
1497 self.failUnlessEqual(statuscode, str(http.FOUND))
1498 self.failUnlessEqual(target, redir_url)
1499 d.addCallback(lambda res:
1500 self.shouldRedirect2("test_POST_FILEURL_check_and_repair",
1503 t="check", repair="true",
1504 when_done=redir_url))
1505 d.addCallback(lambda res:
1506 self.POST(bar_url, t="check", return_to=redir_url))
1508 self.failUnless("Healthy!" in res)
1509 self.failUnless("Return to parent directory" in res)
1510 self.failUnless(redir_url in res)
1511 d.addCallback(_check3)
1514 def test_POST_DIRURL_check(self):
1515 foo_url = self.public_url + "/foo/"
1516 d = self.POST(foo_url, t="check")
1518 self.failUnless("Healthy!" in res)
1519 d.addCallback(_check)
1520 redir_url = "http://allmydata.org/TARGET"
1521 def _check2(statuscode, target):
1522 self.failUnlessEqual(statuscode, str(http.FOUND))
1523 self.failUnlessEqual(target, redir_url)
1524 d.addCallback(lambda res:
1525 self.shouldRedirect2("test_POST_DIRURL_check",
1529 when_done=redir_url))
1530 d.addCallback(lambda res:
1531 self.POST(foo_url, t="check", return_to=redir_url))
1533 self.failUnless("Healthy!" in res)
1534 self.failUnless("Return to parent directory" in res)
1535 self.failUnless(redir_url in res)
1536 d.addCallback(_check3)
1539 def test_POST_DIRURL_check_and_repair(self):
1540 foo_url = self.public_url + "/foo/"
1541 d = self.POST(foo_url, t="check", repair="true")
1543 self.failUnless("Healthy!" in res)
1544 d.addCallback(_check)
1545 redir_url = "http://allmydata.org/TARGET"
1546 def _check2(statuscode, target):
1547 self.failUnlessEqual(statuscode, str(http.FOUND))
1548 self.failUnlessEqual(target, redir_url)
1549 d.addCallback(lambda res:
1550 self.shouldRedirect2("test_POST_DIRURL_check_and_repair",
1553 t="check", repair="true",
1554 when_done=redir_url))
1555 d.addCallback(lambda res:
1556 self.POST(foo_url, t="check", return_to=redir_url))
1558 self.failUnless("Healthy!" in res)
1559 self.failUnless("Return to parent directory" in res)
1560 self.failUnless(redir_url in res)
1561 d.addCallback(_check3)
1564 def wait_for_operation(self, ignored, ophandle):
1565 url = "/operations/" + ophandle
1566 url += "?t=status&output=JSON"
1569 data = simplejson.loads(res)
1570 if not data["finished"]:
1571 d = self.stall(delay=1.0)
1572 d.addCallback(self.wait_for_operation, ophandle)
1578 def get_operation_results(self, ignored, ophandle, output=None):
1579 url = "/operations/" + ophandle
1582 url += "&output=" + output
1585 if output and output.lower() == "json":
1586 return simplejson.loads(res)
1591 def test_POST_DIRURL_deepcheck_no_ophandle(self):
1592 d = self.shouldFail2(error.Error,
1593 "test_POST_DIRURL_deepcheck_no_ophandle",
1595 "slow operation requires ophandle=",
1596 self.POST, self.public_url, t="start-deep-check")
1599 def test_POST_DIRURL_deepcheck(self):
1600 def _check_redirect(statuscode, target):
1601 self.failUnlessEqual(statuscode, str(http.FOUND))
1602 self.failUnless(target.endswith("/operations/123?t=status"))
1603 d = self.shouldRedirect2("test_POST_DIRURL_deepcheck", _check_redirect,
1604 self.POST, self.public_url,
1605 t="start-deep-check", ophandle="123")
1606 d.addCallback(self.wait_for_operation, "123")
1607 def _check_json(data):
1608 self.failUnlessEqual(data["finished"], True)
1609 self.failUnlessEqual(data["count-objects-checked"], 8)
1610 self.failUnlessEqual(data["count-objects-healthy"], 8)
1611 d.addCallback(_check_json)
1612 d.addCallback(self.get_operation_results, "123", "html")
1613 def _check_html(res):
1614 self.failUnless("Objects Checked: <span>8</span>" in res)
1615 self.failUnless("Objects Healthy: <span>8</span>" in res)
1616 d.addCallback(_check_html)
1619 def test_POST_DIRURL_deepcheck_and_repair(self):
1620 d = self.POST(self.public_url, t="start-deep-check", repair="true",
1621 ophandle="124", output="json", followRedirect=True)
1622 d.addCallback(self.wait_for_operation, "124")
1623 def _check_json(data):
1624 self.failUnlessEqual(data["finished"], True)
1625 self.failUnlessEqual(data["count-objects-checked"], 8)
1626 self.failUnlessEqual(data["count-objects-healthy-pre-repair"], 8)
1627 self.failUnlessEqual(data["count-objects-unhealthy-pre-repair"], 0)
1628 self.failUnlessEqual(data["count-corrupt-shares-pre-repair"], 0)
1629 self.failUnlessEqual(data["count-repairs-attempted"], 0)
1630 self.failUnlessEqual(data["count-repairs-successful"], 0)
1631 self.failUnlessEqual(data["count-repairs-unsuccessful"], 0)
1632 self.failUnlessEqual(data["count-objects-healthy-post-repair"], 8)
1633 self.failUnlessEqual(data["count-objects-unhealthy-post-repair"], 0)
1634 self.failUnlessEqual(data["count-corrupt-shares-post-repair"], 0)
1635 d.addCallback(_check_json)
1636 d.addCallback(self.get_operation_results, "124", "html")
1637 def _check_html(res):
1638 self.failUnless("Objects Checked: <span>8</span>" in res)
1640 self.failUnless("Objects Healthy (before repair): <span>8</span>" in res)
1641 self.failUnless("Objects Unhealthy (before repair): <span>0</span>" in res)
1642 self.failUnless("Corrupt Shares (before repair): <span>0</span>" in res)
1644 self.failUnless("Repairs Attempted: <span>0</span>" in res)
1645 self.failUnless("Repairs Successful: <span>0</span>" in res)
1646 self.failUnless("Repairs Unsuccessful: <span>0</span>" in res)
1648 self.failUnless("Objects Healthy (after repair): <span>8</span>" in res)
1649 self.failUnless("Objects Unhealthy (after repair): <span>0</span>" in res)
1650 self.failUnless("Corrupt Shares (after repair): <span>0</span>" in res)
1651 d.addCallback(_check_html)
1654 def test_POST_FILEURL_bad_t(self):
1655 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1656 "POST to file: bad t=bogus",
1657 self.POST, self.public_url + "/foo/bar.txt",
1661 def test_POST_mkdir(self): # return value?
1662 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1663 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1664 d.addCallback(self.failUnlessNodeKeysAre, [])
1667 def test_POST_mkdir_2(self):
1668 d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "")
1669 d.addCallback(lambda res:
1670 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
1671 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1672 d.addCallback(self.failUnlessNodeKeysAre, [])
1675 def test_POST_mkdirs_2(self):
1676 d = self.POST(self.public_url + "/foo/bardir/newdir?t=mkdir", "")
1677 d.addCallback(lambda res:
1678 self.failUnlessNodeHasChild(self._foo_node, u"bardir"))
1679 d.addCallback(lambda res: self._foo_node.get(u"bardir"))
1680 d.addCallback(lambda bardirnode: bardirnode.get(u"newdir"))
1681 d.addCallback(self.failUnlessNodeKeysAre, [])
1684 def test_POST_mkdir_no_parentdir_noredirect(self):
1685 d = self.POST("/uri?t=mkdir")
1686 def _after_mkdir(res):
1687 uri.NewDirectoryURI.init_from_string(res)
1688 d.addCallback(_after_mkdir)
1691 def test_POST_mkdir_no_parentdir_redirect(self):
1692 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1693 d.addBoth(self.shouldRedirect, None, statuscode='303')
1694 def _check_target(target):
1695 target = urllib.unquote(target)
1696 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1697 d.addCallback(_check_target)
1700 def test_POST_noparent_bad(self):
1701 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1702 "/uri accepts only PUT, PUT?t=mkdir, "
1703 "POST?t=upload, and POST?t=mkdir",
1704 self.POST, "/uri?t=bogus")
1707 def test_welcome_page_mkdir_button(self):
1708 # Fetch the welcome page.
1710 def _after_get_welcome_page(res):
1711 MKDIR_BUTTON_RE=re.compile('<form action="([^"]*)" method="post".*<input type="hidden" name="t" value="([^"]*)" /><input type="hidden" name="([^"]*)" value="([^"]*)" /><input type="submit" value="Create Directory!" />', re.I)
1712 mo = MKDIR_BUTTON_RE.search(res)
1713 formaction = mo.group(1)
1715 formaname = mo.group(3)
1716 formavalue = mo.group(4)
1717 return (formaction, formt, formaname, formavalue)
1718 d.addCallback(_after_get_welcome_page)
1719 def _after_parse_form(res):
1720 (formaction, formt, formaname, formavalue) = res
1721 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1722 d.addCallback(_after_parse_form)
1723 d.addBoth(self.shouldRedirect, None, statuscode='303')
1726 def test_POST_mkdir_replace(self): # return value?
1727 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1728 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1729 d.addCallback(self.failUnlessNodeKeysAre, [])
1732 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1733 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1734 d.addBoth(self.shouldFail, error.Error,
1735 "POST_mkdir_no_replace_queryarg",
1737 "There was already a child by that name, and you asked me "
1738 "to not replace it")
1739 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1740 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1743 def test_POST_mkdir_no_replace_field(self): # return value?
1744 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1746 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1748 "There was already a child by that name, and you asked me "
1749 "to not replace it")
1750 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1751 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1754 def test_POST_mkdir_whendone_field(self):
1755 d = self.POST(self.public_url + "/foo",
1756 t="mkdir", name="newdir", when_done="/THERE")
1757 d.addBoth(self.shouldRedirect, "/THERE")
1758 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1759 d.addCallback(self.failUnlessNodeKeysAre, [])
1762 def test_POST_mkdir_whendone_queryarg(self):
1763 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1764 t="mkdir", name="newdir")
1765 d.addBoth(self.shouldRedirect, "/THERE")
1766 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1767 d.addCallback(self.failUnlessNodeKeysAre, [])
1770 def test_POST_bad_t(self):
1771 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1772 "POST to a directory with bad t=BOGUS",
1773 self.POST, self.public_url + "/foo", t="BOGUS")
1776 def test_POST_set_children(self):
1777 contents9, n9, newuri9 = self.makefile(9)
1778 contents10, n10, newuri10 = self.makefile(10)
1779 contents11, n11, newuri11 = self.makefile(11)
1782 "atomic_added_1": [ "filenode", { "rw_uri": "%s",
1785 "ctime": 1002777696.7564139,
1786 "mtime": 1002777696.7564139
1789 "atomic_added_2": [ "filenode", { "rw_uri": "%s",
1792 "ctime": 1002777696.7564139,
1793 "mtime": 1002777696.7564139
1796 "atomic_added_3": [ "filenode", { "rw_uri": "%s",
1799 "ctime": 1002777696.7564139,
1800 "mtime": 1002777696.7564139
1803 }""" % (newuri9, newuri10, newuri11)
1805 url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
1807 d = client.getPage(url, method="POST", postdata=reqbody)
1809 self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
1810 self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
1811 self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
1813 d.addCallback(_then)
1814 d.addErrback(self.dump_error)
1817 def test_POST_put_uri(self):
1818 contents, n, newuri = self.makefile(8)
1819 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1820 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
1821 d.addCallback(lambda res:
1822 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1826 def test_POST_put_uri_replace(self):
1827 contents, n, newuri = self.makefile(8)
1828 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1829 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
1830 d.addCallback(lambda res:
1831 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1835 def test_POST_put_uri_no_replace_queryarg(self):
1836 contents, n, newuri = self.makefile(8)
1837 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1838 name="bar.txt", uri=newuri)
1839 d.addBoth(self.shouldFail, error.Error,
1840 "POST_put_uri_no_replace_queryarg",
1842 "There was already a child by that name, and you asked me "
1843 "to not replace it")
1844 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1845 d.addCallback(self.failUnlessIsBarDotTxt)
1848 def test_POST_put_uri_no_replace_field(self):
1849 contents, n, newuri = self.makefile(8)
1850 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1851 name="bar.txt", uri=newuri)
1852 d.addBoth(self.shouldFail, error.Error,
1853 "POST_put_uri_no_replace_field",
1855 "There was already a child by that name, and you asked me "
1856 "to not replace it")
1857 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1858 d.addCallback(self.failUnlessIsBarDotTxt)
1861 def test_POST_delete(self):
1862 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1863 d.addCallback(lambda res: self._foo_node.list())
1864 def _check(children):
1865 self.failIf(u"bar.txt" in children)
1866 d.addCallback(_check)
1869 def test_POST_rename_file(self):
1870 d = self.POST(self.public_url + "/foo", t="rename",
1871 from_name="bar.txt", to_name='wibble.txt')
1872 d.addCallback(lambda res:
1873 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1874 d.addCallback(lambda res:
1875 self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
1876 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1877 d.addCallback(self.failUnlessIsBarDotTxt)
1878 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1879 d.addCallback(self.failUnlessIsBarJSON)
1882 def test_POST_rename_file_replace(self):
1883 # rename a file and replace a directory with it
1884 d = self.POST(self.public_url + "/foo", t="rename",
1885 from_name="bar.txt", to_name='empty')
1886 d.addCallback(lambda res:
1887 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1888 d.addCallback(lambda res:
1889 self.failUnlessNodeHasChild(self._foo_node, u"empty"))
1890 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1891 d.addCallback(self.failUnlessIsBarDotTxt)
1892 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1893 d.addCallback(self.failUnlessIsBarJSON)
1896 def test_POST_rename_file_no_replace_queryarg(self):
1897 # rename a file and replace a directory with it
1898 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1899 from_name="bar.txt", to_name='empty')
1900 d.addBoth(self.shouldFail, error.Error,
1901 "POST_rename_file_no_replace_queryarg",
1903 "There was already a child by that name, and you asked me "
1904 "to not replace it")
1905 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1906 d.addCallback(self.failUnlessIsEmptyJSON)
1909 def test_POST_rename_file_no_replace_field(self):
1910 # rename a file and replace a directory with it
1911 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1912 from_name="bar.txt", to_name='empty')
1913 d.addBoth(self.shouldFail, error.Error,
1914 "POST_rename_file_no_replace_field",
1916 "There was already a child by that name, and you asked me "
1917 "to not replace it")
1918 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1919 d.addCallback(self.failUnlessIsEmptyJSON)
1922 def failUnlessIsEmptyJSON(self, res):
1923 data = simplejson.loads(res)
1924 self.failUnlessEqual(data[0], "dirnode", data)
1925 self.failUnlessEqual(len(data[1]["children"]), 0)
1927 def test_POST_rename_file_slash_fail(self):
1928 d = self.POST(self.public_url + "/foo", t="rename",
1929 from_name="bar.txt", to_name='kirk/spock.txt')
1930 d.addBoth(self.shouldFail, error.Error,
1931 "test_POST_rename_file_slash_fail",
1933 "to_name= may not contain a slash",
1935 d.addCallback(lambda res:
1936 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1939 def test_POST_rename_dir(self):
1940 d = self.POST(self.public_url, t="rename",
1941 from_name="foo", to_name='plunk')
1942 d.addCallback(lambda res:
1943 self.failIfNodeHasChild(self.public_root, u"foo"))
1944 d.addCallback(lambda res:
1945 self.failUnlessNodeHasChild(self.public_root, u"plunk"))
1946 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1947 d.addCallback(self.failUnlessIsFooJSON)
1950 def shouldRedirect(self, res, target=None, statuscode=None, which=""):
1951 """ If target is not None then the redirection has to go to target. If
1952 statuscode is not None then the redirection has to be accomplished with
1953 that HTTP status code."""
1954 if not isinstance(res, failure.Failure):
1955 to_where = (target is None) and "somewhere" or ("to " + target)
1956 self.fail("%s: we were expecting to get redirected %s, not get an"
1957 " actual page: %s" % (which, to_where, res))
1958 res.trap(error.PageRedirect)
1959 if statuscode is not None:
1960 self.failUnlessEqual(res.value.status, statuscode,
1961 "%s: not a redirect" % which)
1962 if target is not None:
1963 # the PageRedirect does not seem to capture the uri= query arg
1964 # properly, so we can't check for it.
1965 realtarget = self.webish_url + target
1966 self.failUnlessEqual(res.value.location, realtarget,
1967 "%s: wrong target" % which)
1968 return res.value.location
1970 def test_GET_URI_form(self):
1971 base = "/uri?uri=%s" % self._bar_txt_uri
1972 # this is supposed to give us a redirect to /uri/$URI, plus arguments
1973 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
1975 d.addBoth(self.shouldRedirect, targetbase)
1976 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
1977 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
1978 d.addCallback(lambda res: self.GET(base+"&t=json"))
1979 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
1980 d.addCallback(self.log, "about to get file by uri")
1981 d.addCallback(lambda res: self.GET(base, followRedirect=True))
1982 d.addCallback(self.failUnlessIsBarDotTxt)
1983 d.addCallback(self.log, "got file by uri, about to get dir by uri")
1984 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
1985 followRedirect=True))
1986 d.addCallback(self.failUnlessIsFooJSON)
1987 d.addCallback(self.log, "got dir by uri")
1991 def test_GET_URI_form_bad(self):
1992 d = self.shouldFail2(error.Error, "test_GET_URI_form_bad",
1993 "400 Bad Request", "GET /uri requires uri=",
1997 def test_GET_rename_form(self):
1998 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
1999 followRedirect=True)
2001 self.failUnless('name="when_done" value="."' in res, res)
2002 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
2003 d.addCallback(_check)
2006 def log(self, res, msg):
2007 #print "MSG: %s RES: %s" % (msg, res)
2011 def test_GET_URI_URL(self):
2012 base = "/uri/%s" % self._bar_txt_uri
2014 d.addCallback(self.failUnlessIsBarDotTxt)
2015 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
2016 d.addCallback(self.failUnlessIsBarDotTxt)
2017 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
2018 d.addCallback(self.failUnlessIsBarDotTxt)
2021 def test_GET_URI_URL_dir(self):
2022 base = "/uri/%s?t=json" % self._foo_uri
2024 d.addCallback(self.failUnlessIsFooJSON)
2027 def test_GET_URI_URL_missing(self):
2028 base = "/uri/%s" % self._bad_file_uri
2030 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
2031 http.GONE, response_substring="NotEnoughSharesError")
2032 # TODO: how can we exercise both sides of WebDownloadTarget.fail
2033 # here? we must arrange for a download to fail after target.open()
2034 # has been called, and then inspect the response to see that it is
2035 # shorter than we expected.
2038 def test_PUT_NEWFILEURL_uri(self):
2039 contents, n, new_uri = self.makefile(8)
2040 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
2041 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
2042 d.addCallback(lambda res:
2043 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
2047 def test_PUT_NEWFILEURL_uri_replace(self):
2048 contents, n, new_uri = self.makefile(8)
2049 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
2050 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
2051 d.addCallback(lambda res:
2052 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
2056 def test_PUT_NEWFILEURL_uri_no_replace(self):
2057 contents, n, new_uri = self.makefile(8)
2058 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
2059 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
2061 "There was already a child by that name, and you asked me "
2062 "to not replace it")
2065 def test_PUT_NEWFILE_URI(self):
2066 file_contents = "New file contents here\n"
2067 d = self.PUT("/uri", file_contents)
2069 self.failUnless(uri in FakeCHKFileNode.all_contents)
2070 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2072 return self.GET("/uri/%s" % uri)
2073 d.addCallback(_check)
2075 self.failUnlessEqual(res, file_contents)
2076 d.addCallback(_check2)
2079 def test_PUT_NEWFILE_URI_only_PUT(self):
2080 d = self.PUT("/uri?t=bogus", "")
2081 d.addBoth(self.shouldFail, error.Error,
2082 "PUT_NEWFILE_URI_only_PUT",
2084 "/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, and POST?t=mkdir")
2087 def test_PUT_NEWFILE_URI_mutable(self):
2088 file_contents = "New file contents here\n"
2089 d = self.PUT("/uri?mutable=true", file_contents)
2090 def _check_mutable(uri):
2093 self.failUnless(IMutableFileURI.providedBy(u))
2094 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
2095 n = self.s.create_node_from_uri(uri)
2096 return n.download_best_version()
2097 d.addCallback(_check_mutable)
2098 def _check2_mutable(data):
2099 self.failUnlessEqual(data, file_contents)
2100 d.addCallback(_check2_mutable)
2104 self.failUnless(uri in FakeCHKFileNode.all_contents)
2105 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2107 return self.GET("/uri/%s" % uri)
2108 d.addCallback(_check)
2110 self.failUnlessEqual(res, file_contents)
2111 d.addCallback(_check2)
2114 def test_PUT_mkdir(self):
2115 d = self.PUT("/uri?t=mkdir", "")
2117 n = self.s.create_node_from_uri(uri.strip())
2118 d2 = self.failUnlessNodeKeysAre(n, [])
2119 d2.addCallback(lambda res:
2120 self.GET("/uri/%s?t=json" % uri))
2122 d.addCallback(_check)
2123 d.addCallback(self.failUnlessIsEmptyJSON)
2126 def test_POST_check(self):
2127 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
2129 # this returns a string form of the results, which are probably
2130 # None since we're using fake filenodes.
2131 # TODO: verify that the check actually happened, by changing
2132 # FakeCHKFileNode to count how many times .check() has been
2135 d.addCallback(_done)
2138 def test_bad_method(self):
2139 url = self.webish_url + self.public_url + "/foo/bar.txt"
2140 d = self.shouldHTTPError2("test_bad_method",
2141 501, "Not Implemented",
2142 "I don't know how to treat a BOGUS request.",
2143 client.getPage, url, method="BOGUS")
2146 def test_short_url(self):
2147 url = self.webish_url + "/uri"
2148 d = self.shouldHTTPError2("test_short_url", 501, "Not Implemented",
2149 "I don't know how to treat a DELETE request.",
2150 client.getPage, url, method="DELETE")
2153 def test_bad_ophandle(self):
2154 url = self.webish_url + "/operations/bogus?t=status"
2155 d = self.shouldHTTPError2("test_bad_ophandle", 400, "400 Bad Request",
2156 "unknown/expired handle 'bogus'",
2157 client.getPage, url)
2160 def test_incident(self):
2161 d = self.POST("/report_incident", details="eek")
2163 self.failUnless("Thank you for your report!" in res, res)
2164 d.addCallback(_done)
2168 class Util(unittest.TestCase):
2169 def test_abbreviate_time(self):
2170 self.failUnlessEqual(common.abbreviate_time(None), "")
2171 self.failUnlessEqual(common.abbreviate_time(1.234), "1.23s")
2172 self.failUnlessEqual(common.abbreviate_time(0.123), "123ms")
2173 self.failUnlessEqual(common.abbreviate_time(0.00123), "1.2ms")
2174 self.failUnlessEqual(common.abbreviate_time(0.000123), "123us")
2176 def test_abbreviate_rate(self):
2177 self.failUnlessEqual(common.abbreviate_rate(None), "")
2178 self.failUnlessEqual(common.abbreviate_rate(1234000), "1.23MBps")
2179 self.failUnlessEqual(common.abbreviate_rate(12340), "12.3kBps")
2180 self.failUnlessEqual(common.abbreviate_rate(123), "123Bps")
2182 def test_abbreviate_size(self):
2183 self.failUnlessEqual(common.abbreviate_size(None), "")
2184 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000*1000), "1.23GB")
2185 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000), "1.23MB")
2186 self.failUnlessEqual(common.abbreviate_size(1230), "1.2kB")
2187 self.failUnlessEqual(common.abbreviate_size(123), "123B")