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
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], "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(data[1]["children"])
218 self.failUnlessEqual(kidnames,
219 [u"bar.txt", u"blockingfile", u"empty",
220 u"n\u00fc.txt", u"sub"])
221 kids = data[1]["children"]
222 self.failUnlessEqual(kids[u"sub"][0], "dirnode")
223 self.failUnless("metadata" in kids[u"sub"][1])
224 self.failUnless("ctime" in kids[u"sub"][1]["metadata"])
225 self.failUnless("mtime" in kids[u"sub"][1]["metadata"])
226 self.failUnlessEqual(kids[u"bar.txt"][0], "filenode")
227 self.failUnlessEqual(kids[u"bar.txt"][1]["size"], len(self.BAR_CONTENTS))
228 self.failUnlessEqual(kids[u"bar.txt"][1]["ro_uri"], self._bar_txt_uri)
229 self.failUnlessEqual(kids[u"bar.txt"][1]["metadata"]["ctime"],
230 self._bar_txt_metadata["ctime"])
231 self.failUnlessEqual(kids[u"n\u00fc.txt"][1]["ro_uri"],
234 def GET(self, urlpath, followRedirect=False):
235 assert not isinstance(urlpath, unicode)
236 url = self.webish_url + urlpath
237 return client.getPage(url, method="GET", followRedirect=followRedirect)
239 def HEAD(self, urlpath):
240 # this requires some surgery, because twisted.web.client doesn't want
241 # to give us back the response headers.
242 factory = HTTPClientHEADFactory(urlpath, method="HEAD")
243 reactor.connectTCP("localhost", self.webish_port, factory)
244 return factory.deferred
246 def PUT(self, urlpath, data):
247 url = self.webish_url + urlpath
248 return client.getPage(url, method="PUT", postdata=data)
250 def DELETE(self, urlpath):
251 url = self.webish_url + urlpath
252 return client.getPage(url, method="DELETE")
254 def POST(self, urlpath, followRedirect=False, **fields):
255 url = self.webish_url + urlpath
256 sepbase = "boogabooga"
260 form.append('Content-Disposition: form-data; name="_charset"')
264 for name, value in fields.iteritems():
265 if isinstance(value, tuple):
266 filename, value = value
267 form.append('Content-Disposition: form-data; name="%s"; '
268 'filename="%s"' % (name, filename.encode("utf-8")))
270 form.append('Content-Disposition: form-data; name="%s"' % name)
272 if isinstance(value, unicode):
273 value = value.encode("utf-8")
276 assert isinstance(value, str)
280 body = "\r\n".join(form) + "\r\n"
281 headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
283 return client.getPage(url, method="POST", postdata=body,
284 headers=headers, followRedirect=followRedirect)
286 def shouldFail(self, res, expected_failure, which,
287 substring=None, response_substring=None):
288 if isinstance(res, failure.Failure):
289 res.trap(expected_failure)
291 self.failUnless(substring in str(res),
292 "substring '%s' not in '%s'"
293 % (substring, str(res)))
294 if response_substring:
295 self.failUnless(response_substring in res.value.response,
296 "response substring '%s' not in '%s'"
297 % (response_substring, res.value.response))
299 self.fail("%s was supposed to raise %s, not get '%s'" %
300 (which, expected_failure, res))
302 def shouldFail2(self, expected_failure, which, substring,
304 callable, *args, **kwargs):
305 assert substring is None or isinstance(substring, str)
306 assert response_substring is None or isinstance(response_substring, str)
307 d = defer.maybeDeferred(callable, *args, **kwargs)
309 if isinstance(res, failure.Failure):
310 res.trap(expected_failure)
312 self.failUnless(substring in str(res),
313 "%s: substring '%s' not in '%s'"
314 % (which, substring, str(res)))
315 if response_substring:
316 self.failUnless(response_substring in res.value.response,
317 "%s: response substring '%s' not in '%s'"
319 response_substring, res.value.response))
321 self.fail("%s was supposed to raise %s, not get '%s'" %
322 (which, expected_failure, res))
326 def should404(self, res, which):
327 if isinstance(res, failure.Failure):
328 res.trap(error.Error)
329 self.failUnlessEqual(res.value.status, "404")
331 self.fail("%s was supposed to Error(404), not get '%s'" %
334 def shouldHTTPError(self, res, which, code=None, substring=None,
335 response_substring=None):
336 if isinstance(res, failure.Failure):
337 res.trap(error.Error)
339 self.failUnlessEqual(res.value.status, str(code))
341 self.failUnless(substring in str(res),
342 "substring '%s' not in '%s'"
343 % (substring, str(res)))
344 if response_substring:
345 self.failUnless(response_substring in res.value.response,
346 "response substring '%s' not in '%s'"
347 % (response_substring, res.value.response))
349 self.fail("%s was supposed to Error(%s), not get '%s'" %
352 def shouldHTTPError2(self, which,
353 code=None, substring=None, response_substring=None,
354 callable=None, *args, **kwargs):
355 assert substring is None or isinstance(substring, str)
357 d = defer.maybeDeferred(callable, *args, **kwargs)
358 d.addBoth(self.shouldHTTPError, which,
359 code, substring, response_substring)
363 class Web(WebMixin, unittest.TestCase):
364 def test_create(self):
367 def test_welcome(self):
370 self.failUnless('Welcome To AllMyData' in res)
371 self.failUnless('Tahoe' in res)
373 self.s.basedir = 'web/test_welcome'
374 fileutil.make_dirs("web/test_welcome")
375 fileutil.make_dirs("web/test_welcome/private")
377 d.addCallback(_check)
380 def test_provisioning_math(self):
381 self.failUnlessEqual(provisioning.binomial(10, 0), 1)
382 self.failUnlessEqual(provisioning.binomial(10, 1), 10)
383 self.failUnlessEqual(provisioning.binomial(10, 2), 45)
384 self.failUnlessEqual(provisioning.binomial(10, 9), 10)
385 self.failUnlessEqual(provisioning.binomial(10, 10), 1)
387 def test_provisioning(self):
388 d = self.GET("/provisioning/")
390 self.failUnless('Tahoe Provisioning Tool' in res)
391 fields = {'filled': True,
392 "num_users": int(50e3),
393 "files_per_user": 1000,
394 "space_per_user": int(1e9),
395 "sharing_ratio": 1.0,
396 "encoding_parameters": "3-of-10-5",
398 "ownership_mode": "A",
399 "download_rate": 100,
404 return self.POST("/provisioning/", **fields)
406 d.addCallback(_check)
408 self.failUnless('Tahoe Provisioning Tool' in res)
409 self.failUnless("Share space consumed: 167.01TB" in res)
411 fields = {'filled': True,
412 "num_users": int(50e6),
413 "files_per_user": 1000,
414 "space_per_user": int(5e9),
415 "sharing_ratio": 1.0,
416 "encoding_parameters": "25-of-100-50",
417 "num_servers": 30000,
418 "ownership_mode": "E",
419 "drive_failure_model": "U",
421 "download_rate": 1000,
426 return self.POST("/provisioning/", **fields)
427 d.addCallback(_check2)
429 self.failUnless("Share space consumed: huge!" in res)
430 fields = {'filled': True}
431 return self.POST("/provisioning/", **fields)
432 d.addCallback(_check3)
434 self.failUnless("Share space consumed:" in res)
435 d.addCallback(_check4)
438 def test_status(self):
439 dl_num = self.s.list_all_download_statuses()[0].get_counter()
440 ul_num = self.s.list_all_upload_statuses()[0].get_counter()
441 mu_num = self.s.list_all_mapupdate_statuses()[0].get_counter()
442 pub_num = self.s.list_all_publish_statuses()[0].get_counter()
443 ret_num = self.s.list_all_retrieve_statuses()[0].get_counter()
444 d = self.GET("/status", followRedirect=True)
446 self.failUnless('Upload and Download Status' in res, res)
447 self.failUnless('"down-%d"' % dl_num in res, res)
448 self.failUnless('"up-%d"' % ul_num in res, res)
449 self.failUnless('"mapupdate-%d"' % mu_num in res, res)
450 self.failUnless('"publish-%d"' % pub_num in res, res)
451 self.failUnless('"retrieve-%d"' % ret_num in res, res)
452 d.addCallback(_check)
453 d.addCallback(lambda res: self.GET("/status/?t=json"))
454 def _check_json(res):
455 data = simplejson.loads(res)
456 self.failUnless(isinstance(data, dict))
457 active = data["active"]
458 # TODO: test more. We need a way to fake an active operation
460 d.addCallback(_check_json)
462 d.addCallback(lambda res: self.GET("/status/down-%d" % dl_num))
464 self.failUnless("File Download Status" in res, res)
465 d.addCallback(_check_dl)
466 d.addCallback(lambda res: self.GET("/status/up-%d" % ul_num))
468 self.failUnless("File Upload Status" in res, res)
469 d.addCallback(_check_ul)
470 d.addCallback(lambda res: self.GET("/status/mapupdate-%d" % mu_num))
471 def _check_mapupdate(res):
472 self.failUnless("Mutable File Servermap Update Status" in res, res)
473 d.addCallback(_check_mapupdate)
474 d.addCallback(lambda res: self.GET("/status/publish-%d" % pub_num))
475 def _check_publish(res):
476 self.failUnless("Mutable File Publish Status" in res, res)
477 d.addCallback(_check_publish)
478 d.addCallback(lambda res: self.GET("/status/retrieve-%d" % ret_num))
479 def _check_retrieve(res):
480 self.failUnless("Mutable File Retrieve Status" in res, res)
481 d.addCallback(_check_retrieve)
485 def test_status_numbers(self):
486 drrm = status.DownloadResultsRendererMixin()
487 self.failUnlessEqual(drrm.render_time(None, None), "")
488 self.failUnlessEqual(drrm.render_time(None, 2.5), "2.50s")
489 self.failUnlessEqual(drrm.render_time(None, 0.25), "250ms")
490 self.failUnlessEqual(drrm.render_time(None, 0.0021), "2.1ms")
491 self.failUnlessEqual(drrm.render_time(None, 0.000123), "123us")
492 self.failUnlessEqual(drrm.render_rate(None, None), "")
493 self.failUnlessEqual(drrm.render_rate(None, 2500000), "2.50MBps")
494 self.failUnlessEqual(drrm.render_rate(None, 30100), "30.1kBps")
495 self.failUnlessEqual(drrm.render_rate(None, 123), "123Bps")
497 urrm = status.UploadResultsRendererMixin()
498 self.failUnlessEqual(urrm.render_time(None, None), "")
499 self.failUnlessEqual(urrm.render_time(None, 2.5), "2.50s")
500 self.failUnlessEqual(urrm.render_time(None, 0.25), "250ms")
501 self.failUnlessEqual(urrm.render_time(None, 0.0021), "2.1ms")
502 self.failUnlessEqual(urrm.render_time(None, 0.000123), "123us")
503 self.failUnlessEqual(urrm.render_rate(None, None), "")
504 self.failUnlessEqual(urrm.render_rate(None, 2500000), "2.50MBps")
505 self.failUnlessEqual(urrm.render_rate(None, 30100), "30.1kBps")
506 self.failUnlessEqual(urrm.render_rate(None, 123), "123Bps")
508 def test_GET_FILEURL(self):
509 d = self.GET(self.public_url + "/foo/bar.txt")
510 d.addCallback(self.failUnlessIsBarDotTxt)
513 def test_HEAD_FILEURL(self):
514 d = self.HEAD(self.public_url + "/foo/bar.txt")
516 self.failUnlessEqual(headers["content-length"][0],
517 str(len(self.BAR_CONTENTS)))
518 self.failUnlessEqual(headers["content-type"], ["text/plain"])
522 def test_GET_FILEURL_named(self):
523 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
524 base2 = "/named/%s" % urllib.quote(self._bar_txt_uri)
525 d = self.GET(base + "/@@name=/blah.txt")
526 d.addCallback(self.failUnlessIsBarDotTxt)
527 d.addCallback(lambda res: self.GET(base + "/blah.txt"))
528 d.addCallback(self.failUnlessIsBarDotTxt)
529 d.addCallback(lambda res: self.GET(base + "/ignore/lots/blah.txt"))
530 d.addCallback(self.failUnlessIsBarDotTxt)
531 d.addCallback(lambda res: self.GET(base2 + "/@@name=/blah.txt"))
532 d.addCallback(self.failUnlessIsBarDotTxt)
533 save_url = base + "?save=true&filename=blah.txt"
534 d.addCallback(lambda res: self.GET(save_url))
535 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
536 u_filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
537 u_fn_e = urllib.quote(u_filename.encode("utf-8"))
538 u_url = base + "?save=true&filename=" + u_fn_e
539 d.addCallback(lambda res: self.GET(u_url))
540 d.addCallback(self.failUnlessIsBarDotTxt) # TODO: check headers
543 def test_PUT_FILEURL_named_bad(self):
544 base = "/file/%s" % urllib.quote(self._bar_txt_uri)
545 d = self.shouldFail2(error.Error, "test_PUT_FILEURL_named_bad",
547 "/file can only be used with GET or HEAD",
548 self.PUT, base + "/@@name=/blah.txt", "")
551 def test_GET_DIRURL_named_bad(self):
552 base = "/file/%s" % urllib.quote(self._foo_uri)
553 d = self.shouldFail2(error.Error, "test_PUT_DIRURL_named_bad",
556 self.GET, base + "/@@name=/blah.txt")
559 def test_GET_slash_file_bad(self):
560 d = self.shouldFail2(error.Error, "test_GET_slash_file_bad",
562 "/file must be followed by a file-cap and a name",
566 def test_GET_unhandled_URI_named(self):
567 contents, n, newuri = self.makefile(12)
568 verifier_cap = n.get_verifier().to_string()
569 base = "/file/%s" % urllib.quote(verifier_cap)
570 # client.create_node_from_uri() can't handle verify-caps
571 d = self.shouldFail2(error.Error, "GET_unhandled_URI_named",
573 "is not a valid file- or directory- cap",
577 def test_GET_unhandled_URI(self):
578 contents, n, newuri = self.makefile(12)
579 verifier_cap = n.get_verifier().to_string()
580 base = "/uri/%s" % urllib.quote(verifier_cap)
581 # client.create_node_from_uri() can't handle verify-caps
582 d = self.shouldFail2(error.Error, "test_GET_unhandled_URI",
584 "is not a valid file- or directory- cap",
588 def test_GET_FILE_URI(self):
589 base = "/uri/%s" % urllib.quote(self._bar_txt_uri)
591 d.addCallback(self.failUnlessIsBarDotTxt)
594 def test_GET_FILE_URI_badchild(self):
595 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
596 errmsg = "Files have no children, certainly not named 'boguschild'"
597 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
598 "400 Bad Request", errmsg,
602 def test_PUT_FILE_URI_badchild(self):
603 base = "/uri/%s/boguschild" % urllib.quote(self._bar_txt_uri)
604 errmsg = "Cannot create directory 'boguschild', because its parent is a file, not a directory"
605 d = self.shouldFail2(error.Error, "test_GET_FILE_URI_badchild",
606 "400 Bad Request", errmsg,
610 def test_GET_FILEURL_save(self):
611 d = self.GET(self.public_url + "/foo/bar.txt?filename=bar.txt&save=true")
612 # TODO: look at the headers, expect a Content-Disposition: attachment
614 d.addCallback(self.failUnlessIsBarDotTxt)
617 def test_GET_FILEURL_missing(self):
618 d = self.GET(self.public_url + "/foo/missing")
619 d.addBoth(self.should404, "test_GET_FILEURL_missing")
622 def test_PUT_NEWFILEURL(self):
623 d = self.PUT(self.public_url + "/foo/new.txt", self.NEWFILE_CONTENTS)
624 # TODO: we lose the response code, so we can't check this
625 #self.failUnlessEqual(responsecode, 201)
626 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
627 d.addCallback(lambda res:
628 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
629 self.NEWFILE_CONTENTS))
632 def test_PUT_NEWFILEURL_mutable(self):
633 d = self.PUT(self.public_url + "/foo/new.txt?mutable=true",
634 self.NEWFILE_CONTENTS)
635 # TODO: we lose the response code, so we can't check this
636 #self.failUnlessEqual(responsecode, 201)
638 u = uri.from_string_mutable_filenode(res)
639 self.failUnless(u.is_mutable())
640 self.failIf(u.is_readonly())
642 d.addCallback(_check_uri)
643 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
644 d.addCallback(lambda res:
645 self.failUnlessMutableChildContentsAre(self._foo_node,
647 self.NEWFILE_CONTENTS))
650 def test_PUT_NEWFILEURL_mutable_toobig(self):
651 d = self.shouldFail2(error.Error, "test_PUT_NEWFILEURL_mutable_toobig",
652 "413 Request Entity Too Large",
653 "SDMF is limited to one segment, and 10001 > 10000",
655 self.public_url + "/foo/new.txt?mutable=true",
656 "b" * (self.s.MUTABLE_SIZELIMIT+1))
659 def test_PUT_NEWFILEURL_replace(self):
660 d = self.PUT(self.public_url + "/foo/bar.txt", self.NEWFILE_CONTENTS)
661 # TODO: we lose the response code, so we can't check this
662 #self.failUnlessEqual(responsecode, 200)
663 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
664 d.addCallback(lambda res:
665 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
666 self.NEWFILE_CONTENTS))
669 def test_PUT_NEWFILEURL_bad_t(self):
670 d = self.shouldFail2(error.Error, "PUT_bad_t", "400 Bad Request",
671 "PUT to a file: bad t=bogus",
672 self.PUT, self.public_url + "/foo/bar.txt?t=bogus",
676 def test_PUT_NEWFILEURL_no_replace(self):
677 d = self.PUT(self.public_url + "/foo/bar.txt?replace=false",
678 self.NEWFILE_CONTENTS)
679 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_no_replace",
681 "There was already a child by that name, and you asked me "
685 def test_PUT_NEWFILEURL_mkdirs(self):
686 d = self.PUT(self.public_url + "/foo/newdir/new.txt", self.NEWFILE_CONTENTS)
688 d.addCallback(self.failUnlessURIMatchesChild, fn, u"newdir/new.txt")
689 d.addCallback(lambda res: self.failIfNodeHasChild(fn, u"new.txt"))
690 d.addCallback(lambda res: self.failUnlessNodeHasChild(fn, u"newdir"))
691 d.addCallback(lambda res:
692 self.failUnlessChildContentsAre(fn, u"newdir/new.txt",
693 self.NEWFILE_CONTENTS))
696 def test_PUT_NEWFILEURL_blocked(self):
697 d = self.PUT(self.public_url + "/foo/blockingfile/new.txt",
698 self.NEWFILE_CONTENTS)
699 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_blocked",
701 "Unable to create directory 'blockingfile': a file was in the way")
704 def test_DELETE_FILEURL(self):
705 d = self.DELETE(self.public_url + "/foo/bar.txt")
706 d.addCallback(lambda res:
707 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
710 def test_DELETE_FILEURL_missing(self):
711 d = self.DELETE(self.public_url + "/foo/missing")
712 d.addBoth(self.should404, "test_DELETE_FILEURL_missing")
715 def test_DELETE_FILEURL_missing2(self):
716 d = self.DELETE(self.public_url + "/missing/missing")
717 d.addBoth(self.should404, "test_DELETE_FILEURL_missing2")
720 def test_GET_FILEURL_json(self):
721 # twisted.web.http.parse_qs ignores any query args without an '=', so
722 # I can't do "GET /path?json", I have to do "GET /path/t=json"
723 # instead. This may make it tricky to emulate the S3 interface
725 d = self.GET(self.public_url + "/foo/bar.txt?t=json")
726 d.addCallback(self.failUnlessIsBarJSON)
729 def test_GET_FILEURL_json_missing(self):
730 d = self.GET(self.public_url + "/foo/missing?json")
731 d.addBoth(self.should404, "test_GET_FILEURL_json_missing")
734 def test_GET_FILEURL_uri(self):
735 d = self.GET(self.public_url + "/foo/bar.txt?t=uri")
737 self.failUnlessEqual(res, self._bar_txt_uri)
738 d.addCallback(_check)
739 d.addCallback(lambda res:
740 self.GET(self.public_url + "/foo/bar.txt?t=readonly-uri"))
742 # for now, for files, uris and readonly-uris are the same
743 self.failUnlessEqual(res, self._bar_txt_uri)
744 d.addCallback(_check2)
747 def test_GET_FILEURL_badtype(self):
748 d = self.shouldHTTPError2("GET t=bogus", 400, "Bad Request",
751 self.public_url + "/foo/bar.txt?t=bogus")
754 def test_GET_FILEURL_uri_missing(self):
755 d = self.GET(self.public_url + "/foo/missing?t=uri")
756 d.addBoth(self.should404, "test_GET_FILEURL_uri_missing")
759 def test_GET_DIRURL(self):
760 # the addSlash means we get a redirect here
761 # from /uri/$URI/foo/ , we need ../../../ to get back to the root
763 d = self.GET(self.public_url + "/foo", followRedirect=True)
765 self.failUnless(('<a href="%s">Return to Welcome page' % ROOT)
767 # the FILE reference points to a URI, but it should end in bar.txt
768 bar_url = ("%s/file/%s/@@named=/bar.txt" %
769 (ROOT, urllib.quote(self._bar_txt_uri)))
770 get_bar = "".join([r'<td>',
771 r'<a href="%s">bar.txt</a>' % bar_url,
774 r'\s+<td>%d</td>' % len(self.BAR_CONTENTS),
776 self.failUnless(re.search(get_bar, res), res)
777 for line in res.split("\n"):
778 # find the line that contains the delete button for bar.txt
779 if ("form action" in line and
780 'value="delete"' in line and
781 'value="bar.txt"' in line):
782 # the form target should use a relative URL
783 foo_url = urllib.quote("%s/uri/%s/" % (ROOT, self._foo_uri))
784 self.failUnless(('action="%s"' % foo_url) in line, line)
785 # and the when_done= should too
786 #done_url = urllib.quote(???)
787 #self.failUnless(('name="when_done" value="%s"' % done_url)
791 self.fail("unable to find delete-bar.txt line", res)
793 # the DIR reference just points to a URI
794 sub_url = ("%s/uri/%s/" % (ROOT, urllib.quote(self._sub_uri)))
795 get_sub = ((r'<td><a href="%s">sub</a></td>' % sub_url)
796 + r'\s+<td>DIR</td>')
797 self.failUnless(re.search(get_sub, res), res)
798 d.addCallback(_check)
800 # look at a directory which is readonly
801 d.addCallback(lambda res:
802 self.GET(self.public_url + "/reedownlee", followRedirect=True))
804 self.failUnless("(readonly)" in res, res)
805 self.failIf("Upload a file" in res, res)
806 d.addCallback(_check2)
808 # and at a directory that contains a readonly directory
809 d.addCallback(lambda res:
810 self.GET(self.public_url, followRedirect=True))
812 self.failUnless(re.search(r'<td><a href="[\.\/]+/uri/URI%3ADIR2-RO%3A[^"]+">reedownlee</a>'
813 '</td>\s+<td>DIR-RO</td>', res))
814 d.addCallback(_check3)
818 def test_GET_DIRURL_badtype(self):
819 d = self.shouldHTTPError2("test_GET_DIRURL_badtype",
823 self.public_url + "/foo?t=bogus")
826 def test_GET_DIRURL_json(self):
827 d = self.GET(self.public_url + "/foo?t=json")
828 d.addCallback(self.failUnlessIsFooJSON)
831 def test_GET_DIRURL_manifest(self):
832 d = self.GET(self.public_url + "/foo?t=manifest", followRedirect=True)
834 self.failUnless("Refresh Capabilities" in manifest)
838 def test_GET_DIRURL_deepsize(self):
839 d = self.GET(self.public_url + "/foo?t=deep-size", followRedirect=True)
841 self.failUnless(re.search(r'^\d+$', manifest), manifest)
842 self.failUnlessEqual("57", manifest)
846 def test_GET_DIRURL_deepstats(self):
847 d = self.GET(self.public_url + "/foo?t=deep-stats", followRedirect=True)
848 def _got(stats_json):
849 stats = simplejson.loads(stats_json)
850 expected = {"count-immutable-files": 3,
851 "count-mutable-files": 0,
852 "count-literal-files": 0,
854 "count-directories": 3,
855 "size-immutable-files": 57,
856 "size-literal-files": 0,
857 #"size-directories": 1912, # varies
858 #"largest-directory": 1590,
859 "largest-directory-children": 5,
860 "largest-immutable-file": 19,
862 for k,v in expected.iteritems():
863 self.failUnlessEqual(stats[k], v,
864 "stats[%s] was %s, not %s" %
866 self.failUnlessEqual(stats["size-files-histogram"],
871 def test_GET_DIRURL_uri(self):
872 d = self.GET(self.public_url + "/foo?t=uri")
874 self.failUnlessEqual(res, self._foo_uri)
875 d.addCallback(_check)
878 def test_GET_DIRURL_readonly_uri(self):
879 d = self.GET(self.public_url + "/foo?t=readonly-uri")
881 self.failUnlessEqual(res, self._foo_readonly_uri)
882 d.addCallback(_check)
885 def test_PUT_NEWDIRURL(self):
886 d = self.PUT(self.public_url + "/foo/newdir?t=mkdir", "")
887 d.addCallback(lambda res:
888 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
889 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
890 d.addCallback(self.failUnlessNodeKeysAre, [])
893 def test_PUT_NEWDIRURL_exists(self):
894 d = self.PUT(self.public_url + "/foo/sub?t=mkdir", "")
895 d.addCallback(lambda res:
896 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
897 d.addCallback(lambda res: self._foo_node.get(u"sub"))
898 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
901 def test_PUT_NEWDIRURL_blocked(self):
902 d = self.shouldFail2(error.Error, "PUT_NEWDIRURL_blocked",
903 "409 Conflict", "Unable to create directory 'bar.txt': a file was in the way",
905 self.public_url + "/foo/bar.txt/sub?t=mkdir", "")
906 d.addCallback(lambda res:
907 self.failUnlessNodeHasChild(self._foo_node, u"sub"))
908 d.addCallback(lambda res: self._foo_node.get(u"sub"))
909 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
912 def test_PUT_NEWDIRURL_mkdir_p(self):
913 d = defer.succeed(None)
914 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t='mkdir', name='mkp'))
915 d.addCallback(lambda res: self.failUnlessNodeHasChild(self._foo_node, u"mkp"))
916 d.addCallback(lambda res: self._foo_node.get(u"mkp"))
917 def mkdir_p(mkpnode):
918 url = '/uri/%s?t=mkdir-p&path=/sub1/sub2' % urllib.quote(mkpnode.get_uri())
920 def made_subsub(ssuri):
921 d = self._foo_node.get_child_at_path(u"mkp/sub1/sub2")
922 d.addCallback(lambda ssnode: self.failUnlessEqual(ssnode.get_uri(), ssuri))
924 d.addCallback(lambda uri2: self.failUnlessEqual(uri2, ssuri))
926 d.addCallback(made_subsub)
928 d.addCallback(mkdir_p)
931 def test_PUT_NEWDIRURL_mkdirs(self):
932 d = self.PUT(self.public_url + "/foo/subdir/newdir?t=mkdir", "")
933 d.addCallback(lambda res:
934 self.failIfNodeHasChild(self._foo_node, u"newdir"))
935 d.addCallback(lambda res:
936 self.failUnlessNodeHasChild(self._foo_node, u"subdir"))
937 d.addCallback(lambda res:
938 self._foo_node.get_child_at_path(u"subdir/newdir"))
939 d.addCallback(self.failUnlessNodeKeysAre, [])
942 def test_DELETE_DIRURL(self):
943 d = self.DELETE(self.public_url + "/foo")
944 d.addCallback(lambda res:
945 self.failIfNodeHasChild(self.public_root, u"foo"))
948 def test_DELETE_DIRURL_missing(self):
949 d = self.DELETE(self.public_url + "/foo/missing")
950 d.addBoth(self.should404, "test_DELETE_DIRURL_missing")
951 d.addCallback(lambda res:
952 self.failUnlessNodeHasChild(self.public_root, u"foo"))
955 def test_DELETE_DIRURL_missing2(self):
956 d = self.DELETE(self.public_url + "/missing")
957 d.addBoth(self.should404, "test_DELETE_DIRURL_missing2")
962 w = webish.DirnodeWalkerMixin()
963 def visitor(childpath, childnode, metadata):
965 d = w.walk(self.public_root, visitor)
968 def failUnlessNodeKeysAre(self, node, expected_keys):
969 for k in expected_keys:
970 assert isinstance(k, unicode)
972 def _check(children):
973 self.failUnlessEqual(sorted(children.keys()), sorted(expected_keys))
974 d.addCallback(_check)
976 def failUnlessNodeHasChild(self, node, name):
977 assert isinstance(name, unicode)
979 def _check(children):
980 self.failUnless(name in children)
981 d.addCallback(_check)
983 def failIfNodeHasChild(self, node, name):
984 assert isinstance(name, unicode)
986 def _check(children):
987 self.failIf(name in children)
988 d.addCallback(_check)
991 def failUnlessChildContentsAre(self, node, name, expected_contents):
992 assert isinstance(name, unicode)
993 d = node.get_child_at_path(name)
994 d.addCallback(lambda node: node.download_to_data())
995 def _check(contents):
996 self.failUnlessEqual(contents, expected_contents)
997 d.addCallback(_check)
1000 def failUnlessMutableChildContentsAre(self, node, name, expected_contents):
1001 assert isinstance(name, unicode)
1002 d = node.get_child_at_path(name)
1003 d.addCallback(lambda node: node.download_best_version())
1004 def _check(contents):
1005 self.failUnlessEqual(contents, expected_contents)
1006 d.addCallback(_check)
1009 def failUnlessChildURIIs(self, node, name, expected_uri):
1010 assert isinstance(name, unicode)
1011 d = node.get_child_at_path(name)
1013 self.failUnlessEqual(child.get_uri(), expected_uri.strip())
1014 d.addCallback(_check)
1017 def failUnlessURIMatchesChild(self, got_uri, node, name):
1018 assert isinstance(name, unicode)
1019 d = node.get_child_at_path(name)
1021 self.failUnlessEqual(got_uri.strip(), child.get_uri())
1022 d.addCallback(_check)
1025 def failUnlessCHKURIHasContents(self, got_uri, contents):
1026 self.failUnless(FakeCHKFileNode.all_contents[got_uri] == contents)
1028 def test_POST_upload(self):
1029 d = self.POST(self.public_url + "/foo", t="upload",
1030 file=("new.txt", self.NEWFILE_CONTENTS))
1032 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1033 d.addCallback(lambda res:
1034 self.failUnlessChildContentsAre(fn, u"new.txt",
1035 self.NEWFILE_CONTENTS))
1038 def test_POST_upload_unicode(self):
1039 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1040 d = self.POST(self.public_url + "/foo", t="upload",
1041 file=(filename, self.NEWFILE_CONTENTS))
1043 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1044 d.addCallback(lambda res:
1045 self.failUnlessChildContentsAre(fn, filename,
1046 self.NEWFILE_CONTENTS))
1047 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1048 d.addCallback(lambda res: self.GET(target_url))
1049 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1050 self.NEWFILE_CONTENTS,
1054 def test_POST_upload_unicode_named(self):
1055 filename = u"n\u00e9wer.txt" # n e-acute w e r . t x t
1056 d = self.POST(self.public_url + "/foo", t="upload",
1058 file=("overridden", self.NEWFILE_CONTENTS))
1060 d.addCallback(self.failUnlessURIMatchesChild, fn, filename)
1061 d.addCallback(lambda res:
1062 self.failUnlessChildContentsAre(fn, filename,
1063 self.NEWFILE_CONTENTS))
1064 target_url = self.public_url + "/foo/" + filename.encode("utf-8")
1065 d.addCallback(lambda res: self.GET(target_url))
1066 d.addCallback(lambda contents: self.failUnlessEqual(contents,
1067 self.NEWFILE_CONTENTS,
1071 def test_POST_upload_no_link(self):
1072 d = self.POST("/uri", t="upload",
1073 file=("new.txt", self.NEWFILE_CONTENTS))
1074 def _check_upload_results(page):
1075 # this should be a page which describes the results of the upload
1076 # that just finished.
1077 self.failUnless("Upload Results:" in page)
1078 self.failUnless("URI:" in page)
1079 uri_re = re.compile("URI: <tt><span>(.*)</span>")
1080 mo = uri_re.search(page)
1081 self.failUnless(mo, page)
1082 new_uri = mo.group(1)
1084 d.addCallback(_check_upload_results)
1085 d.addCallback(self.failUnlessCHKURIHasContents, self.NEWFILE_CONTENTS)
1088 def test_POST_upload_no_link_whendone(self):
1089 d = self.POST("/uri", t="upload", when_done="/",
1090 file=("new.txt", self.NEWFILE_CONTENTS))
1091 d.addBoth(self.shouldRedirect, "/")
1094 def shouldRedirect2(self, which, checker, callable, *args, **kwargs):
1095 d = defer.maybeDeferred(callable, *args, **kwargs)
1097 if isinstance(res, failure.Failure):
1098 res.trap(error.PageRedirect)
1099 statuscode = res.value.status
1100 target = res.value.location
1101 return checker(statuscode, target)
1102 self.fail("%s: callable was supposed to redirect, not return '%s'"
1107 def test_POST_upload_no_link_whendone_results(self):
1108 def check(statuscode, target):
1109 self.failUnlessEqual(statuscode, str(http.FOUND))
1110 self.failUnless(target.startswith(self.webish_url), target)
1111 return client.getPage(target, method="GET")
1112 d = self.shouldRedirect2("test_POST_upload_no_link_whendone_results",
1114 self.POST, "/uri", t="upload",
1115 when_done="/uri/%(uri)s",
1116 file=("new.txt", self.NEWFILE_CONTENTS))
1117 d.addCallback(lambda res:
1118 self.failUnlessEqual(res, self.NEWFILE_CONTENTS))
1121 def test_POST_upload_no_link_mutable(self):
1122 d = self.POST("/uri", t="upload", mutable="true",
1123 file=("new.txt", self.NEWFILE_CONTENTS))
1124 def _check(new_uri):
1125 new_uri = new_uri.strip()
1126 self.new_uri = new_uri
1128 self.failUnless(IMutableFileURI.providedBy(u))
1129 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
1130 n = self.s.create_node_from_uri(new_uri)
1131 return n.download_best_version()
1132 d.addCallback(_check)
1134 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1135 return self.GET("/uri/%s" % urllib.quote(self.new_uri))
1136 d.addCallback(_check2)
1138 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1139 return self.GET("/file/%s" % urllib.quote(self.new_uri))
1140 d.addCallback(_check3)
1142 self.failUnlessEqual(data, self.NEWFILE_CONTENTS)
1143 d.addCallback(_check4)
1146 def test_POST_upload_no_link_mutable_toobig(self):
1147 d = self.shouldFail2(error.Error,
1148 "test_POST_upload_no_link_mutable_toobig",
1149 "413 Request Entity Too Large",
1150 "SDMF is limited to one segment, and 10001 > 10000",
1152 "/uri", t="upload", mutable="true",
1154 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1157 def test_POST_upload_mutable(self):
1158 # this creates a mutable file
1159 d = self.POST(self.public_url + "/foo", t="upload", mutable="true",
1160 file=("new.txt", self.NEWFILE_CONTENTS))
1162 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1163 d.addCallback(lambda res:
1164 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1165 self.NEWFILE_CONTENTS))
1166 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1168 self.failUnless(IMutableFileNode.providedBy(newnode))
1169 self.failUnless(newnode.is_mutable())
1170 self.failIf(newnode.is_readonly())
1171 self._mutable_node = newnode
1172 self._mutable_uri = newnode.get_uri()
1175 # now upload it again and make sure that the URI doesn't change
1176 NEWER_CONTENTS = self.NEWFILE_CONTENTS + "newer\n"
1177 d.addCallback(lambda res:
1178 self.POST(self.public_url + "/foo", t="upload",
1180 file=("new.txt", NEWER_CONTENTS)))
1181 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1182 d.addCallback(lambda res:
1183 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1185 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1187 self.failUnless(IMutableFileNode.providedBy(newnode))
1188 self.failUnless(newnode.is_mutable())
1189 self.failIf(newnode.is_readonly())
1190 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1191 d.addCallback(_got2)
1193 # upload a second time, using PUT instead of POST
1194 NEW2_CONTENTS = NEWER_CONTENTS + "overwrite with PUT\n"
1195 d.addCallback(lambda res:
1196 self.PUT(self.public_url + "/foo/new.txt", NEW2_CONTENTS))
1197 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1198 d.addCallback(lambda res:
1199 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1202 # finally list the directory, since mutable files are displayed
1205 d.addCallback(lambda res:
1206 self.GET(self.public_url + "/foo/",
1207 followRedirect=True))
1208 def _check_page(res):
1209 # TODO: assert more about the contents
1210 self.failUnless("Overwrite" in res)
1211 self.failUnless("Choose new file:" in res)
1213 d.addCallback(_check_page)
1215 # test that clicking on the "overwrite" button works
1216 EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
1217 def _parse_overwrite_form_and_submit(res):
1219 OVERWRITE_FORM_RE=re.compile('<form action="([^"]*)" method="post" .*<input type="hidden" name="t" value="upload" /><input type="hidden" name="when_done" value="([^"]*)" />', re.I)
1220 mo = OVERWRITE_FORM_RE.search(res)
1221 self.failUnless(mo, "overwrite form not found in '" + res +
1222 "', in which the overwrite form was not found")
1223 formaction=mo.group(1)
1224 formwhendone=mo.group(2)
1226 fileurl = "../../../uri/" + urllib.quote(self._mutable_uri)
1227 self.failUnlessEqual(formaction, fileurl)
1228 # to POST, we need to absoluteify the URL
1229 new_formaction = "/uri/%s" % urllib.quote(self._mutable_uri)
1230 self.failUnlessEqual(formwhendone,
1231 "../uri/%s/" % urllib.quote(self._foo_uri))
1232 return self.POST(new_formaction,
1234 file=("new.txt", EVEN_NEWER_CONTENTS),
1235 when_done=formwhendone,
1236 followRedirect=False)
1237 d.addCallback(_parse_overwrite_form_and_submit)
1238 # This will redirect us to ../uri/$FOOURI, rather than
1239 # ../uri/$PARENT/foo, but apparently twisted.web.client absolutifies
1240 # the redirect for us, and remember that shouldRedirect prepends
1241 # self.webish_url for us.
1242 d.addBoth(self.shouldRedirect,
1243 "/uri/%s/" % urllib.quote(self._foo_uri),
1244 which="test_POST_upload_mutable.overwrite")
1245 d.addCallback(lambda res:
1246 self.failUnlessMutableChildContentsAre(fn, u"new.txt",
1247 EVEN_NEWER_CONTENTS))
1248 d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
1250 self.failUnless(IMutableFileNode.providedBy(newnode))
1251 self.failUnless(newnode.is_mutable())
1252 self.failIf(newnode.is_readonly())
1253 self.failUnlessEqual(self._mutable_uri, newnode.get_uri())
1254 d.addCallback(_got3)
1256 # look at the JSON form of the enclosing directory
1257 d.addCallback(lambda res:
1258 self.GET(self.public_url + "/foo/?t=json",
1259 followRedirect=True))
1260 def _check_page_json(res):
1261 parsed = simplejson.loads(res)
1262 self.failUnlessEqual(parsed[0], "dirnode")
1263 children = parsed[1]["children"]
1264 self.failUnless("new.txt" in children)
1265 new_json = children["new.txt"]
1266 self.failUnlessEqual(new_json[0], "filenode")
1267 self.failUnless(new_json[1]["mutable"])
1268 self.failUnlessEqual(new_json[1]["rw_uri"], self._mutable_uri)
1269 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1270 self.failUnlessEqual(new_json[1]["ro_uri"], ro_uri)
1271 d.addCallback(_check_page_json)
1273 # and the JSON form of the file
1274 d.addCallback(lambda res:
1275 self.GET(self.public_url + "/foo/new.txt?t=json"))
1276 def _check_file_json(res):
1277 parsed = simplejson.loads(res)
1278 self.failUnlessEqual(parsed[0], "filenode")
1279 self.failUnless(parsed[1]["mutable"])
1280 self.failUnlessEqual(parsed[1]["rw_uri"], self._mutable_uri)
1281 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1282 self.failUnlessEqual(parsed[1]["ro_uri"], ro_uri)
1283 d.addCallback(_check_file_json)
1285 # and look at t=uri and t=readonly-uri
1286 d.addCallback(lambda res:
1287 self.GET(self.public_url + "/foo/new.txt?t=uri"))
1288 d.addCallback(lambda res: self.failUnlessEqual(res, self._mutable_uri))
1289 d.addCallback(lambda res:
1290 self.GET(self.public_url + "/foo/new.txt?t=readonly-uri"))
1291 def _check_ro_uri(res):
1292 ro_uri = unicode(self._mutable_node.get_readonly().to_string())
1293 self.failUnlessEqual(res, ro_uri)
1294 d.addCallback(_check_ro_uri)
1296 # make sure we can get to it from /uri/URI
1297 d.addCallback(lambda res:
1298 self.GET("/uri/%s" % urllib.quote(self._mutable_uri)))
1299 d.addCallback(lambda res:
1300 self.failUnlessEqual(res, EVEN_NEWER_CONTENTS))
1302 # and that HEAD computes the size correctly
1303 d.addCallback(lambda res:
1304 self.HEAD(self.public_url + "/foo/new.txt"))
1305 def _got_headers(headers):
1306 self.failUnlessEqual(headers["content-length"][0],
1307 str(len(EVEN_NEWER_CONTENTS)))
1308 self.failUnlessEqual(headers["content-type"], ["text/plain"])
1309 d.addCallback(_got_headers)
1311 # make sure that size errors are displayed correctly for overwrite
1312 d.addCallback(lambda res:
1313 self.shouldFail2(error.Error,
1314 "test_POST_upload_mutable-toobig",
1315 "413 Request Entity Too Large",
1316 "SDMF is limited to one segment, and 10001 > 10000",
1318 self.public_url + "/foo", t="upload",
1321 "b" * (self.s.MUTABLE_SIZELIMIT+1)),
1324 d.addErrback(self.dump_error)
1327 def test_POST_upload_mutable_toobig(self):
1328 d = self.shouldFail2(error.Error,
1329 "test_POST_upload_no_link_mutable_toobig",
1330 "413 Request Entity Too Large",
1331 "SDMF is limited to one segment, and 10001 > 10000",
1333 self.public_url + "/foo",
1334 t="upload", mutable="true",
1336 "b" * (self.s.MUTABLE_SIZELIMIT+1)) )
1339 def dump_error(self, f):
1340 # if the web server returns an error code (like 400 Bad Request),
1341 # web.client.getPage puts the HTTP response body into the .response
1342 # attribute of the exception object that it gives back. It does not
1343 # appear in the Failure's repr(), so the ERROR that trial displays
1344 # will be rather terse and unhelpful. addErrback this method to the
1345 # end of your chain to get more information out of these errors.
1346 if f.check(error.Error):
1347 print "web.error.Error:"
1349 print f.value.response
1352 def test_POST_upload_replace(self):
1353 d = self.POST(self.public_url + "/foo", t="upload",
1354 file=("bar.txt", self.NEWFILE_CONTENTS))
1356 d.addCallback(self.failUnlessURIMatchesChild, fn, u"bar.txt")
1357 d.addCallback(lambda res:
1358 self.failUnlessChildContentsAre(fn, u"bar.txt",
1359 self.NEWFILE_CONTENTS))
1362 def test_POST_upload_no_replace_ok(self):
1363 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1364 file=("new.txt", self.NEWFILE_CONTENTS))
1365 d.addCallback(lambda res: self.GET(self.public_url + "/foo/new.txt"))
1366 d.addCallback(lambda res: self.failUnlessEqual(res,
1367 self.NEWFILE_CONTENTS))
1370 def test_POST_upload_no_replace_queryarg(self):
1371 d = self.POST(self.public_url + "/foo?replace=false", t="upload",
1372 file=("bar.txt", self.NEWFILE_CONTENTS))
1373 d.addBoth(self.shouldFail, error.Error,
1374 "POST_upload_no_replace_queryarg",
1376 "There was already a child by that name, and you asked me "
1377 "to not replace it")
1378 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1379 d.addCallback(self.failUnlessIsBarDotTxt)
1382 def test_POST_upload_no_replace_field(self):
1383 d = self.POST(self.public_url + "/foo", t="upload", replace="false",
1384 file=("bar.txt", self.NEWFILE_CONTENTS))
1385 d.addBoth(self.shouldFail, error.Error, "POST_upload_no_replace_field",
1387 "There was already a child by that name, and you asked me "
1388 "to not replace it")
1389 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1390 d.addCallback(self.failUnlessIsBarDotTxt)
1393 def test_POST_upload_whendone(self):
1394 d = self.POST(self.public_url + "/foo", t="upload", when_done="/THERE",
1395 file=("new.txt", self.NEWFILE_CONTENTS))
1396 d.addBoth(self.shouldRedirect, "/THERE")
1398 d.addCallback(lambda res:
1399 self.failUnlessChildContentsAre(fn, u"new.txt",
1400 self.NEWFILE_CONTENTS))
1403 def test_POST_upload_named(self):
1405 d = self.POST(self.public_url + "/foo", t="upload",
1406 name="new.txt", file=self.NEWFILE_CONTENTS)
1407 d.addCallback(self.failUnlessURIMatchesChild, fn, u"new.txt")
1408 d.addCallback(lambda res:
1409 self.failUnlessChildContentsAre(fn, u"new.txt",
1410 self.NEWFILE_CONTENTS))
1413 def test_POST_upload_named_badfilename(self):
1414 d = self.POST(self.public_url + "/foo", t="upload",
1415 name="slashes/are/bad.txt", file=self.NEWFILE_CONTENTS)
1416 d.addBoth(self.shouldFail, error.Error,
1417 "test_POST_upload_named_badfilename",
1419 "name= may not contain a slash",
1421 # make sure that nothing was added
1422 d.addCallback(lambda res:
1423 self.failUnlessNodeKeysAre(self._foo_node,
1424 [u"bar.txt", u"blockingfile",
1425 u"empty", u"n\u00fc.txt",
1429 def test_POST_FILEURL_check(self):
1430 bar_url = self.public_url + "/foo/bar.txt"
1431 d = self.POST(bar_url, t="check")
1433 self.failUnless("Healthy!" in res)
1434 d.addCallback(_check)
1435 redir_url = "http://allmydata.org/TARGET"
1436 def _check2(statuscode, target):
1437 self.failUnlessEqual(statuscode, str(http.FOUND))
1438 self.failUnlessEqual(target, redir_url)
1439 d.addCallback(lambda res:
1440 self.shouldRedirect2("test_POST_FILEURL_check",
1444 when_done=redir_url))
1445 d.addCallback(lambda res:
1446 self.POST(bar_url, t="check", return_to=redir_url))
1448 self.failUnless("Healthy!" in res)
1449 self.failUnless("Return to parent directory" in res)
1450 self.failUnless(redir_url in res)
1451 d.addCallback(_check3)
1454 def test_POST_FILEURL_check_and_repair(self):
1455 bar_url = self.public_url + "/foo/bar.txt"
1456 d = self.POST(bar_url, t="check", repair="true")
1458 self.failUnless("Healthy!" in res)
1459 d.addCallback(_check)
1460 redir_url = "http://allmydata.org/TARGET"
1461 def _check2(statuscode, target):
1462 self.failUnlessEqual(statuscode, str(http.FOUND))
1463 self.failUnlessEqual(target, redir_url)
1464 d.addCallback(lambda res:
1465 self.shouldRedirect2("test_POST_FILEURL_check_and_repair",
1468 t="check", repair="true",
1469 when_done=redir_url))
1470 d.addCallback(lambda res:
1471 self.POST(bar_url, t="check", return_to=redir_url))
1473 self.failUnless("Healthy!" in res)
1474 self.failUnless("Return to parent directory" in res)
1475 self.failUnless(redir_url in res)
1476 d.addCallback(_check3)
1479 def test_POST_DIRURL_check(self):
1480 foo_url = self.public_url + "/foo/"
1481 d = self.POST(foo_url, t="check")
1483 self.failUnless("Healthy!" in res)
1484 d.addCallback(_check)
1485 redir_url = "http://allmydata.org/TARGET"
1486 def _check2(statuscode, target):
1487 self.failUnlessEqual(statuscode, str(http.FOUND))
1488 self.failUnlessEqual(target, redir_url)
1489 d.addCallback(lambda res:
1490 self.shouldRedirect2("test_POST_DIRURL_check",
1494 when_done=redir_url))
1495 d.addCallback(lambda res:
1496 self.POST(foo_url, t="check", return_to=redir_url))
1498 self.failUnless("Healthy!" in res)
1499 self.failUnless("Return to parent directory" in res)
1500 self.failUnless(redir_url in res)
1501 d.addCallback(_check3)
1504 def test_POST_DIRURL_check_and_repair(self):
1505 foo_url = self.public_url + "/foo/"
1506 d = self.POST(foo_url, t="check", repair="true")
1508 self.failUnless("Healthy!" in res)
1509 d.addCallback(_check)
1510 redir_url = "http://allmydata.org/TARGET"
1511 def _check2(statuscode, target):
1512 self.failUnlessEqual(statuscode, str(http.FOUND))
1513 self.failUnlessEqual(target, redir_url)
1514 d.addCallback(lambda res:
1515 self.shouldRedirect2("test_POST_DIRURL_check_and_repair",
1518 t="check", repair="true",
1519 when_done=redir_url))
1520 d.addCallback(lambda res:
1521 self.POST(foo_url, t="check", return_to=redir_url))
1523 self.failUnless("Healthy!" in res)
1524 self.failUnless("Return to parent directory" in res)
1525 self.failUnless(redir_url in res)
1526 d.addCallback(_check3)
1529 def test_POST_DIRURL_deepcheck(self):
1530 d = self.POST(self.public_url, t="deep-check")
1532 self.failUnless("Objects Checked: <span>8</span>" in res)
1533 self.failUnless("Objects Healthy: <span>8</span>" in res)
1534 d.addCallback(_check)
1535 redir_url = "http://allmydata.org/TARGET"
1536 def _check2(statuscode, target):
1537 self.failUnlessEqual(statuscode, str(http.FOUND))
1538 self.failUnlessEqual(target, redir_url)
1539 d.addCallback(lambda res:
1540 self.shouldRedirect2("test_POST_DIRURL_check",
1542 self.POST, self.public_url,
1544 when_done=redir_url))
1545 d.addCallback(lambda res:
1546 self.POST(self.public_url, t="deep-check",
1547 return_to=redir_url))
1549 self.failUnless("Return to parent directory" in res)
1550 self.failUnless(redir_url in res)
1551 d.addCallback(_check3)
1554 def test_POST_DIRURL_deepcheck_and_repair(self):
1555 d = self.POST(self.public_url, t="deep-check", repair="true")
1557 self.failUnless("Objects Checked: <span>8</span>" in res)
1559 self.failUnless("Objects Healthy (before repair): <span>8</span>" in res)
1560 self.failUnless("Objects Unhealthy (before repair): <span>0</span>" in res)
1561 self.failUnless("Corrupt Shares (before repair): <span>0</span>" in res)
1563 self.failUnless("Repairs Attempted: <span>0</span>" in res)
1564 self.failUnless("Repairs Successful: <span>0</span>" in res)
1565 self.failUnless("Repairs Unsuccessful: <span>0</span>" in res)
1567 self.failUnless("Objects Healthy (after repair): <span>8</span>" in res)
1568 self.failUnless("Objects Unhealthy (after repair): <span>0</span>" in res)
1569 self.failUnless("Corrupt Shares (after repair): <span>0</span>" in res)
1570 d.addCallback(_check)
1571 redir_url = "http://allmydata.org/TARGET"
1572 def _check2(statuscode, target):
1573 self.failUnlessEqual(statuscode, str(http.FOUND))
1574 self.failUnlessEqual(target, redir_url)
1575 d.addCallback(lambda res:
1576 self.shouldRedirect2("test_POST_DIRURL_check",
1578 self.POST, self.public_url,
1580 when_done=redir_url))
1581 d.addCallback(lambda res:
1582 self.POST(self.public_url, t="deep-check",
1583 return_to=redir_url))
1585 self.failUnless("Return to parent directory" in res)
1586 self.failUnless(redir_url in res)
1587 d.addCallback(_check3)
1590 def test_POST_FILEURL_bad_t(self):
1591 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1592 "POST to file: bad t=bogus",
1593 self.POST, self.public_url + "/foo/bar.txt",
1597 def test_POST_mkdir(self): # return value?
1598 d = self.POST(self.public_url + "/foo", t="mkdir", name="newdir")
1599 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1600 d.addCallback(self.failUnlessNodeKeysAre, [])
1603 def test_POST_mkdir_2(self):
1604 d = self.POST(self.public_url + "/foo/newdir?t=mkdir", "")
1605 d.addCallback(lambda res:
1606 self.failUnlessNodeHasChild(self._foo_node, u"newdir"))
1607 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1608 d.addCallback(self.failUnlessNodeKeysAre, [])
1611 def test_POST_mkdirs_2(self):
1612 d = self.POST(self.public_url + "/foo/bardir/newdir?t=mkdir", "")
1613 d.addCallback(lambda res:
1614 self.failUnlessNodeHasChild(self._foo_node, u"bardir"))
1615 d.addCallback(lambda res: self._foo_node.get(u"bardir"))
1616 d.addCallback(lambda bardirnode: bardirnode.get(u"newdir"))
1617 d.addCallback(self.failUnlessNodeKeysAre, [])
1620 def test_POST_mkdir_no_parentdir_noredirect(self):
1621 d = self.POST("/uri?t=mkdir")
1622 def _after_mkdir(res):
1623 uri.NewDirectoryURI.init_from_string(res)
1624 d.addCallback(_after_mkdir)
1627 def test_POST_mkdir_no_parentdir_redirect(self):
1628 d = self.POST("/uri?t=mkdir&redirect_to_result=true")
1629 d.addBoth(self.shouldRedirect, None, statuscode='303')
1630 def _check_target(target):
1631 target = urllib.unquote(target)
1632 self.failUnless(target.startswith("uri/URI:DIR2:"), target)
1633 d.addCallback(_check_target)
1636 def test_POST_noparent_bad(self):
1637 d = self.shouldHTTPError2("POST /uri?t=bogus", 400, "Bad Request",
1638 "/uri accepts only PUT, PUT?t=mkdir, "
1639 "POST?t=upload, and POST?t=mkdir",
1640 self.POST, "/uri?t=bogus")
1643 def test_welcome_page_mkdir_button(self):
1644 # Fetch the welcome page.
1646 def _after_get_welcome_page(res):
1647 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)
1648 mo = MKDIR_BUTTON_RE.search(res)
1649 formaction = mo.group(1)
1651 formaname = mo.group(3)
1652 formavalue = mo.group(4)
1653 return (formaction, formt, formaname, formavalue)
1654 d.addCallback(_after_get_welcome_page)
1655 def _after_parse_form(res):
1656 (formaction, formt, formaname, formavalue) = res
1657 return self.POST("/%s?t=%s&%s=%s" % (formaction, formt, formaname, formavalue))
1658 d.addCallback(_after_parse_form)
1659 d.addBoth(self.shouldRedirect, None, statuscode='303')
1662 def test_POST_mkdir_replace(self): # return value?
1663 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub")
1664 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1665 d.addCallback(self.failUnlessNodeKeysAre, [])
1668 def test_POST_mkdir_no_replace_queryarg(self): # return value?
1669 d = self.POST(self.public_url + "/foo?replace=false", t="mkdir", name="sub")
1670 d.addBoth(self.shouldFail, error.Error,
1671 "POST_mkdir_no_replace_queryarg",
1673 "There was already a child by that name, and you asked me "
1674 "to not replace it")
1675 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1676 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1679 def test_POST_mkdir_no_replace_field(self): # return value?
1680 d = self.POST(self.public_url + "/foo", t="mkdir", name="sub",
1682 d.addBoth(self.shouldFail, error.Error, "POST_mkdir_no_replace_field",
1684 "There was already a child by that name, and you asked me "
1685 "to not replace it")
1686 d.addCallback(lambda res: self._foo_node.get(u"sub"))
1687 d.addCallback(self.failUnlessNodeKeysAre, [u"baz.txt"])
1690 def test_POST_mkdir_whendone_field(self):
1691 d = self.POST(self.public_url + "/foo",
1692 t="mkdir", name="newdir", when_done="/THERE")
1693 d.addBoth(self.shouldRedirect, "/THERE")
1694 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1695 d.addCallback(self.failUnlessNodeKeysAre, [])
1698 def test_POST_mkdir_whendone_queryarg(self):
1699 d = self.POST(self.public_url + "/foo?when_done=/THERE",
1700 t="mkdir", name="newdir")
1701 d.addBoth(self.shouldRedirect, "/THERE")
1702 d.addCallback(lambda res: self._foo_node.get(u"newdir"))
1703 d.addCallback(self.failUnlessNodeKeysAre, [])
1706 def test_POST_bad_t(self):
1707 d = self.shouldFail2(error.Error, "POST_bad_t", "400 Bad Request",
1708 "POST to a directory with bad t=BOGUS",
1709 self.POST, self.public_url + "/foo", t="BOGUS")
1712 def test_POST_set_children(self):
1713 contents9, n9, newuri9 = self.makefile(9)
1714 contents10, n10, newuri10 = self.makefile(10)
1715 contents11, n11, newuri11 = self.makefile(11)
1718 "atomic_added_1": [ "filenode", { "rw_uri": "%s",
1721 "ctime": 1002777696.7564139,
1722 "mtime": 1002777696.7564139
1725 "atomic_added_2": [ "filenode", { "rw_uri": "%s",
1728 "ctime": 1002777696.7564139,
1729 "mtime": 1002777696.7564139
1732 "atomic_added_3": [ "filenode", { "rw_uri": "%s",
1735 "ctime": 1002777696.7564139,
1736 "mtime": 1002777696.7564139
1739 }""" % (newuri9, newuri10, newuri11)
1741 url = self.webish_url + self.public_url + "/foo" + "?t=set_children"
1743 d = client.getPage(url, method="POST", postdata=reqbody)
1745 self.failUnlessURIMatchesChild(newuri9, self._foo_node, u"atomic_added_1")
1746 self.failUnlessURIMatchesChild(newuri10, self._foo_node, u"atomic_added_2")
1747 self.failUnlessURIMatchesChild(newuri11, self._foo_node, u"atomic_added_3")
1749 d.addCallback(_then)
1750 d.addErrback(self.dump_error)
1753 def test_POST_put_uri(self):
1754 contents, n, newuri = self.makefile(8)
1755 d = self.POST(self.public_url + "/foo", t="uri", name="new.txt", uri=newuri)
1756 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"new.txt")
1757 d.addCallback(lambda res:
1758 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1762 def test_POST_put_uri_replace(self):
1763 contents, n, newuri = self.makefile(8)
1764 d = self.POST(self.public_url + "/foo", t="uri", name="bar.txt", uri=newuri)
1765 d.addCallback(self.failUnlessURIMatchesChild, self._foo_node, u"bar.txt")
1766 d.addCallback(lambda res:
1767 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
1771 def test_POST_put_uri_no_replace_queryarg(self):
1772 contents, n, newuri = self.makefile(8)
1773 d = self.POST(self.public_url + "/foo?replace=false", t="uri",
1774 name="bar.txt", uri=newuri)
1775 d.addBoth(self.shouldFail, error.Error,
1776 "POST_put_uri_no_replace_queryarg",
1778 "There was already a child by that name, and you asked me "
1779 "to not replace it")
1780 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1781 d.addCallback(self.failUnlessIsBarDotTxt)
1784 def test_POST_put_uri_no_replace_field(self):
1785 contents, n, newuri = self.makefile(8)
1786 d = self.POST(self.public_url + "/foo", t="uri", replace="false",
1787 name="bar.txt", uri=newuri)
1788 d.addBoth(self.shouldFail, error.Error,
1789 "POST_put_uri_no_replace_field",
1791 "There was already a child by that name, and you asked me "
1792 "to not replace it")
1793 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt"))
1794 d.addCallback(self.failUnlessIsBarDotTxt)
1797 def test_POST_delete(self):
1798 d = self.POST(self.public_url + "/foo", t="delete", name="bar.txt")
1799 d.addCallback(lambda res: self._foo_node.list())
1800 def _check(children):
1801 self.failIf(u"bar.txt" in children)
1802 d.addCallback(_check)
1805 def test_POST_rename_file(self):
1806 d = self.POST(self.public_url + "/foo", t="rename",
1807 from_name="bar.txt", to_name='wibble.txt')
1808 d.addCallback(lambda res:
1809 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1810 d.addCallback(lambda res:
1811 self.failUnlessNodeHasChild(self._foo_node, u"wibble.txt"))
1812 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt"))
1813 d.addCallback(self.failUnlessIsBarDotTxt)
1814 d.addCallback(lambda res: self.GET(self.public_url + "/foo/wibble.txt?t=json"))
1815 d.addCallback(self.failUnlessIsBarJSON)
1818 def test_POST_rename_file_replace(self):
1819 # rename a file and replace a directory with it
1820 d = self.POST(self.public_url + "/foo", t="rename",
1821 from_name="bar.txt", to_name='empty')
1822 d.addCallback(lambda res:
1823 self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
1824 d.addCallback(lambda res:
1825 self.failUnlessNodeHasChild(self._foo_node, u"empty"))
1826 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty"))
1827 d.addCallback(self.failUnlessIsBarDotTxt)
1828 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1829 d.addCallback(self.failUnlessIsBarJSON)
1832 def test_POST_rename_file_no_replace_queryarg(self):
1833 # rename a file and replace a directory with it
1834 d = self.POST(self.public_url + "/foo?replace=false", t="rename",
1835 from_name="bar.txt", to_name='empty')
1836 d.addBoth(self.shouldFail, error.Error,
1837 "POST_rename_file_no_replace_queryarg",
1839 "There was already a child by that name, and you asked me "
1840 "to not replace it")
1841 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1842 d.addCallback(self.failUnlessIsEmptyJSON)
1845 def test_POST_rename_file_no_replace_field(self):
1846 # rename a file and replace a directory with it
1847 d = self.POST(self.public_url + "/foo", t="rename", replace="false",
1848 from_name="bar.txt", to_name='empty')
1849 d.addBoth(self.shouldFail, error.Error,
1850 "POST_rename_file_no_replace_field",
1852 "There was already a child by that name, and you asked me "
1853 "to not replace it")
1854 d.addCallback(lambda res: self.GET(self.public_url + "/foo/empty?t=json"))
1855 d.addCallback(self.failUnlessIsEmptyJSON)
1858 def failUnlessIsEmptyJSON(self, res):
1859 data = simplejson.loads(res)
1860 self.failUnlessEqual(data[0], "dirnode", data)
1861 self.failUnlessEqual(len(data[1]["children"]), 0)
1863 def test_POST_rename_file_slash_fail(self):
1864 d = self.POST(self.public_url + "/foo", t="rename",
1865 from_name="bar.txt", to_name='kirk/spock.txt')
1866 d.addBoth(self.shouldFail, error.Error,
1867 "test_POST_rename_file_slash_fail",
1869 "to_name= may not contain a slash",
1871 d.addCallback(lambda res:
1872 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1873 d.addCallback(lambda res: self.POST(self.public_url, t="rename",
1874 from_name="foo/bar.txt", to_name='george.txt'))
1875 d.addBoth(self.shouldFail, error.Error,
1876 "test_POST_rename_file_slash_fail",
1878 "from_name= may not contain a slash",
1880 d.addCallback(lambda res:
1881 self.failUnlessNodeHasChild(self.public_root, u"foo"))
1882 d.addCallback(lambda res:
1883 self.failIfNodeHasChild(self.public_root, u"george.txt"))
1884 d.addCallback(lambda res:
1885 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt"))
1886 d.addCallback(lambda res: self.GET(self.public_url + "/foo?t=json"))
1887 d.addCallback(self.failUnlessIsFooJSON)
1890 def test_POST_rename_dir(self):
1891 d = self.POST(self.public_url, t="rename",
1892 from_name="foo", to_name='plunk')
1893 d.addCallback(lambda res:
1894 self.failIfNodeHasChild(self.public_root, u"foo"))
1895 d.addCallback(lambda res:
1896 self.failUnlessNodeHasChild(self.public_root, u"plunk"))
1897 d.addCallback(lambda res: self.GET(self.public_url + "/plunk?t=json"))
1898 d.addCallback(self.failUnlessIsFooJSON)
1901 def shouldRedirect(self, res, target=None, statuscode=None, which=""):
1902 """ If target is not None then the redirection has to go to target. If
1903 statuscode is not None then the redirection has to be accomplished with
1904 that HTTP status code."""
1905 if not isinstance(res, failure.Failure):
1906 to_where = (target is None) and "somewhere" or ("to " + target)
1907 self.fail("%s: we were expecting to get redirected %s, not get an"
1908 " actual page: %s" % (which, to_where, res))
1909 res.trap(error.PageRedirect)
1910 if statuscode is not None:
1911 self.failUnlessEqual(res.value.status, statuscode,
1912 "%s: not a redirect" % which)
1913 if target is not None:
1914 # the PageRedirect does not seem to capture the uri= query arg
1915 # properly, so we can't check for it.
1916 realtarget = self.webish_url + target
1917 self.failUnlessEqual(res.value.location, realtarget,
1918 "%s: wrong target" % which)
1919 return res.value.location
1921 def test_GET_URI_form(self):
1922 base = "/uri?uri=%s" % self._bar_txt_uri
1923 # this is supposed to give us a redirect to /uri/$URI, plus arguments
1924 targetbase = "/uri/%s" % urllib.quote(self._bar_txt_uri)
1926 d.addBoth(self.shouldRedirect, targetbase)
1927 d.addCallback(lambda res: self.GET(base+"&filename=bar.txt"))
1928 d.addBoth(self.shouldRedirect, targetbase+"?filename=bar.txt")
1929 d.addCallback(lambda res: self.GET(base+"&t=json"))
1930 d.addBoth(self.shouldRedirect, targetbase+"?t=json")
1931 d.addCallback(self.log, "about to get file by uri")
1932 d.addCallback(lambda res: self.GET(base, followRedirect=True))
1933 d.addCallback(self.failUnlessIsBarDotTxt)
1934 d.addCallback(self.log, "got file by uri, about to get dir by uri")
1935 d.addCallback(lambda res: self.GET("/uri?uri=%s&t=json" % self._foo_uri,
1936 followRedirect=True))
1937 d.addCallback(self.failUnlessIsFooJSON)
1938 d.addCallback(self.log, "got dir by uri")
1942 def test_GET_URI_form_bad(self):
1943 d = self.shouldFail2(error.Error, "test_GET_URI_form_bad",
1944 "400 Bad Request", "GET /uri requires uri=",
1948 def test_GET_rename_form(self):
1949 d = self.GET(self.public_url + "/foo?t=rename-form&name=bar.txt",
1950 followRedirect=True)
1952 self.failUnless('name="when_done" value="."' in res, res)
1953 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res))
1954 d.addCallback(_check)
1957 def log(self, res, msg):
1958 #print "MSG: %s RES: %s" % (msg, res)
1962 def test_GET_URI_URL(self):
1963 base = "/uri/%s" % self._bar_txt_uri
1965 d.addCallback(self.failUnlessIsBarDotTxt)
1966 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt"))
1967 d.addCallback(self.failUnlessIsBarDotTxt)
1968 d.addCallback(lambda res: self.GET(base+"?filename=bar.txt&save=true"))
1969 d.addCallback(self.failUnlessIsBarDotTxt)
1972 def test_GET_URI_URL_dir(self):
1973 base = "/uri/%s?t=json" % self._foo_uri
1975 d.addCallback(self.failUnlessIsFooJSON)
1978 def test_GET_URI_URL_missing(self):
1979 base = "/uri/%s" % self._bad_file_uri
1981 d.addBoth(self.shouldHTTPError, "test_GET_URI_URL_missing",
1982 http.GONE, response_substring="NotEnoughSharesError")
1983 # TODO: how can we exercise both sides of WebDownloadTarget.fail
1984 # here? we must arrange for a download to fail after target.open()
1985 # has been called, and then inspect the response to see that it is
1986 # shorter than we expected.
1989 def test_PUT_NEWFILEURL_uri(self):
1990 contents, n, new_uri = self.makefile(8)
1991 d = self.PUT(self.public_url + "/foo/new.txt?t=uri", new_uri)
1992 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
1993 d.addCallback(lambda res:
1994 self.failUnlessChildContentsAre(self._foo_node, u"new.txt",
1998 def test_PUT_NEWFILEURL_uri_replace(self):
1999 contents, n, new_uri = self.makefile(8)
2000 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri", new_uri)
2001 d.addCallback(lambda res: self.failUnlessEqual(res.strip(), new_uri))
2002 d.addCallback(lambda res:
2003 self.failUnlessChildContentsAre(self._foo_node, u"bar.txt",
2007 def test_PUT_NEWFILEURL_uri_no_replace(self):
2008 contents, n, new_uri = self.makefile(8)
2009 d = self.PUT(self.public_url + "/foo/bar.txt?t=uri&replace=false", new_uri)
2010 d.addBoth(self.shouldFail, error.Error, "PUT_NEWFILEURL_uri_no_replace",
2012 "There was already a child by that name, and you asked me "
2013 "to not replace it")
2016 def test_PUT_NEWFILE_URI(self):
2017 file_contents = "New file contents here\n"
2018 d = self.PUT("/uri", file_contents)
2020 self.failUnless(uri in FakeCHKFileNode.all_contents)
2021 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2023 return self.GET("/uri/%s" % uri)
2024 d.addCallback(_check)
2026 self.failUnlessEqual(res, file_contents)
2027 d.addCallback(_check2)
2030 def test_PUT_NEWFILE_URI_only_PUT(self):
2031 d = self.PUT("/uri?t=bogus", "")
2032 d.addBoth(self.shouldFail, error.Error,
2033 "PUT_NEWFILE_URI_only_PUT",
2035 "/uri accepts only PUT, PUT?t=mkdir, POST?t=upload, and POST?t=mkdir")
2038 def test_PUT_NEWFILE_URI_mutable(self):
2039 file_contents = "New file contents here\n"
2040 d = self.PUT("/uri?mutable=true", file_contents)
2041 def _check_mutable(uri):
2044 self.failUnless(IMutableFileURI.providedBy(u))
2045 self.failUnless(u.storage_index in FakeMutableFileNode.all_contents)
2046 n = self.s.create_node_from_uri(uri)
2047 return n.download_best_version()
2048 d.addCallback(_check_mutable)
2049 def _check2_mutable(data):
2050 self.failUnlessEqual(data, file_contents)
2051 d.addCallback(_check2_mutable)
2055 self.failUnless(uri in FakeCHKFileNode.all_contents)
2056 self.failUnlessEqual(FakeCHKFileNode.all_contents[uri],
2058 return self.GET("/uri/%s" % uri)
2059 d.addCallback(_check)
2061 self.failUnlessEqual(res, file_contents)
2062 d.addCallback(_check2)
2065 def test_PUT_mkdir(self):
2066 d = self.PUT("/uri?t=mkdir", "")
2068 n = self.s.create_node_from_uri(uri.strip())
2069 d2 = self.failUnlessNodeKeysAre(n, [])
2070 d2.addCallback(lambda res:
2071 self.GET("/uri/%s?t=json" % uri))
2073 d.addCallback(_check)
2074 d.addCallback(self.failUnlessIsEmptyJSON)
2077 def test_POST_check(self):
2078 d = self.POST(self.public_url + "/foo", t="check", name="bar.txt")
2080 # this returns a string form of the results, which are probably
2081 # None since we're using fake filenodes.
2082 # TODO: verify that the check actually happened, by changing
2083 # FakeCHKFileNode to count how many times .check() has been
2086 d.addCallback(_done)
2089 def test_bad_method(self):
2090 url = self.webish_url + self.public_url + "/foo/bar.txt"
2091 d = self.shouldHTTPError2("test_bad_method",
2092 501, "Not Implemented",
2093 "I don't know how to treat a BOGUS request.",
2094 client.getPage, url, method="BOGUS")
2097 def test_short_url(self):
2098 url = self.webish_url + "/uri"
2099 d = self.shouldHTTPError2("test_short_url", 501, "Not Implemented",
2100 "I don't know how to treat a DELETE request.",
2101 client.getPage, url, method="DELETE")
2104 class Util(unittest.TestCase):
2105 def test_abbreviate_time(self):
2106 self.failUnlessEqual(common.abbreviate_time(None), "")
2107 self.failUnlessEqual(common.abbreviate_time(1.234), "1.23s")
2108 self.failUnlessEqual(common.abbreviate_time(0.123), "123ms")
2109 self.failUnlessEqual(common.abbreviate_time(0.00123), "1.2ms")
2110 self.failUnlessEqual(common.abbreviate_time(0.000123), "123us")
2112 def test_abbreviate_rate(self):
2113 self.failUnlessEqual(common.abbreviate_rate(None), "")
2114 self.failUnlessEqual(common.abbreviate_rate(1234000), "1.23MBps")
2115 self.failUnlessEqual(common.abbreviate_rate(12340), "12.3kBps")
2116 self.failUnlessEqual(common.abbreviate_rate(123), "123Bps")
2118 def test_abbreviate_size(self):
2119 self.failUnlessEqual(common.abbreviate_size(None), "")
2120 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000*1000), "1.23GB")
2121 self.failUnlessEqual(common.abbreviate_size(1.23*1000*1000), "1.23MB")
2122 self.failUnlessEqual(common.abbreviate_size(1230), "1.2kB")
2123 self.failUnlessEqual(common.abbreviate_size(123), "123B")